// -*- c++ -*-

// TODO(schwehr): Create an archive of messages that do not decode.

#ifndef LIBAIS_AIS_H_
#define LIBAIS_AIS_H_

#include <array>
#include <bitset>
#include <cassert>
#include <cstring>
#include <string>
#include <iostream>
#include <vector>
#include <memory>
#include <map>
using std::bitset;
using std::ostream;
using std::string;
using std::unique_ptr;
using std::vector;

#define LIBAIS_VERSION_MAJOR 0
#define LIBAIS_VERSION_MINOR 15

extern "C" {
// For configuration scripts to detect libais and the version numbers.
int LibAisVersionMajor();
int LibAisVersionMinor();
}

namespace libais {

// Returns the text in the nth field starting with the first field being 0.
// Empty delim_str is not allowed.
string GetNthField(const string &str, const size_t n, const string &delim_str);

// Returns the number of pad bits in an AIS AIVDM NMEA string.
// Returns -1 if there is an error.
int GetPad(const string &nmea_str);

// Returns the armored payload of an AIS AIVDM NMEA string.
// Returns an empty string if there was an error.
string GetBody(const string &nmea_str);


// Note: Needs to be kept in sync with AIS_STATUS_STRINGS list in ais.cpp.
enum AIS_STATUS {
	AIS_UNINITIALIZED,  // Message is not yet completely decoded.
	AIS_OK,
	AIS_ERR_BAD_BIT_COUNT,
	AIS_ERR_BAD_NMEA_CHR,
	AIS_ERR_BAD_PTR,
	AIS_ERR_UNKNOWN_MSG_TYPE,
	AIS_ERR_MSG_NOT_IMPLEMENTED,
	AIS_ERR_MSG_SUB_NOT_IMPLEMENTED,
	AIS_ERR_EXPECTED_STRING,
	AIS_ERR_BAD_MSG_CONTENT,
	AIS_ERR_MSG_TOO_LONG,
	AIS_ERR_BAD_SUB_MSG,
	AIS_ERR_BAD_SUB_SUB_MSG,
	AIS_STATUS_NUM_CODES
};

extern const char *const AIS_STATUS_STRINGS[AIS_STATUS_NUM_CODES];

// Designated Area Codes (DAC) / Maritime Identification Digits define
// which country controls a subset of the submessage spaces within
// AIS "binary" messages 6, 8, 25, and 26.  See:
//   http://www.itu.int/online/mms/glad/cga_mids.sh?lng=E
//   http://en.wikipedia.org/w/index.php?title=Maritime_identification_digits
// River Information System (RIS):
//   ECE-TRANS-SC3-2006-10r-RIS.pdf

enum Dac {
	AIS_DAC_0_TEST = 0,
	AIS_DAC_1_INTERNATIONAL,
	AIS_DAC_200_RIS = 200,
	AIS_DAC_201_ALBANIA = 201,
	AIS_DAC_202_ANDORRA = 202,
	AIS_DAC_203_AUSTRIA = 203,
	AIS_DAC_204_AZORES = 204,
	AIS_DAC_205_BELGIUM = 205,
	AIS_DAC_206_BELARUS = 206,
	AIS_DAC_207_BULGARIA = 207,
	AIS_DAC_208_VATICAN_CITY_STATE = 208,
	AIS_DAC_209_CYPRUS = 209,
	AIS_DAC_210_CYPRUS = 210,
	AIS_DAC_211_GERMANY = 211,
	AIS_DAC_212_CYPRUS = 212,
	AIS_DAC_213_GEORGIA = 213,
	AIS_DAC_214_MOLDOVA = 214,
	AIS_DAC_215_MALTA = 215,
	AIS_DAC_216_ARMENIA = 216,
	AIS_DAC_218_GERMANY = 218,
	AIS_DAC_219_DENMARK = 219,
	AIS_DAC_220_DENMARK = 220,
	AIS_DAC_224_SPAIN = 224,
	AIS_DAC_225_SPAIN = 225,
	AIS_DAC_226_FRANCE = 226,
	AIS_DAC_227_FRANCE = 227,
	AIS_DAC_228_FRANCE = 228,
	AIS_DAC_229_MALTA = 229,
	AIS_DAC_230_FINLAND = 230,
	AIS_DAC_231_FAROE_ISLANDS = 231,
	AIS_DAC_232_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 232,
	AIS_DAC_233_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 233,
	AIS_DAC_234_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 234,
	AIS_DAC_235_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND = 235,
	AIS_DAC_236_GIBRALTAR = 236,
	AIS_DAC_237_GREECE = 237,
	AIS_DAC_238_CROATIA = 238,
	AIS_DAC_239_GREECE = 239,
	AIS_DAC_240_GREECE = 240,
	AIS_DAC_241_GREECE = 241,
	AIS_DAC_242_MOROCCO = 242,
	AIS_DAC_243_HUNGARY = 243,
	AIS_DAC_244_NETHERLANDS = 244,
	AIS_DAC_245_NETHERLANDS = 245,
	AIS_DAC_246_NETHERLANDS = 246,
	AIS_DAC_247_ITALY = 247,
	AIS_DAC_248_MALTA = 248,
	AIS_DAC_249_MALTA = 249,
	AIS_DAC_250_IRELAND = 250,
	AIS_DAC_251_ICELAND = 251,
	AIS_DAC_252_LIECHTENSTEIN = 252,
	AIS_DAC_253_LUXEMBOURG = 253,
	AIS_DAC_254_MONACO = 254,
	AIS_DAC_255_MADEIRA = 255,
	AIS_DAC_256_MALTA = 256,
	AIS_DAC_257_NORWAY = 257,
	AIS_DAC_258_NORWAY = 258,
	AIS_DAC_259_NORWAY = 259,
	AIS_DAC_261_POLAND = 261,
	AIS_DAC_262_MONTENEGRO = 262,
	AIS_DAC_263_PORTUGAL = 263,
	AIS_DAC_264_ROMANIA = 264,
	AIS_DAC_265_SWEDEN = 265,
	AIS_DAC_266_SWEDEN = 266,
	AIS_DAC_267_SLOVAKIA = 267,
	AIS_DAC_268_SAN_MARINO = 268,
	AIS_DAC_269_SWITZERLAND = 269,
	AIS_DAC_270_CZECH_REPUBLIC = 270,
	AIS_DAC_271_TURKEY = 271,
	AIS_DAC_272_UKRAINE = 272,
	AIS_DAC_273_RUSSIAN_FEDERATION = 273,
	AIS_DAC_274_MACEDONIA = 274,
	AIS_DAC_275_LATVIA = 275,
	AIS_DAC_276_ESTONIA = 276,
	AIS_DAC_277_LITHUANIA = 277,
	AIS_DAC_278_SLOVENIA = 278,
	AIS_DAC_279_SERBIA = 279,
	AIS_DAC_301_ANGUILLA = 301,
	AIS_DAC_303_ALASKA = 303,
	AIS_DAC_304_ANTIGUA_AND_BARBUDA = 304,
	AIS_DAC_305_ANTIGUA_AND_BARBUDA = 305,
	AIS_DAC_306_NETHERLANDS_ANTILLES = 306,
	AIS_DAC_307_ARUBA = 307,
	AIS_DAC_308_BAHAMAS = 308,
	AIS_DAC_309_BAHAMAS = 309,
	AIS_DAC_310_BERMUDA = 310,
	AIS_DAC_311_BAHAMAS = 311,
	AIS_DAC_312_BELIZE = 312,
	AIS_DAC_314_BARBADOS = 314,
	AIS_DAC_316_CANADA = 316,
	AIS_DAC_319_CAYMAN_ISLANDS = 319,
	AIS_DAC_321_COSTA_RICA = 321,
	AIS_DAC_323_CUBA = 323,
	AIS_DAC_325_DOMINICA = 325,
	AIS_DAC_327_DOMINICAN_REPUBLIC = 327,
	AIS_DAC_329_GUADELOUPE = 329,
	AIS_DAC_330_GRENADA = 330,
	AIS_DAC_331_GREENLAND = 331,
	AIS_DAC_332_GUATEMALA = 332,
	AIS_DAC_334_HONDURAS = 334,
	AIS_DAC_336_HAITI = 336,
	AIS_DAC_338_UNITED_STATES_OF_AMERICA = 338,
	AIS_DAC_339_JAMAICA = 339,
	AIS_DAC_341_SAINT_KITTS_AND_NEVIS = 341,
	AIS_DAC_343_SAINT_LUCIA = 343,
	AIS_DAC_345_MEXICO = 345,
	AIS_DAC_347_MARTINIQUE = 347,
	AIS_DAC_348_MONTSERRAT = 348,
	AIS_DAC_350_NICARAGUA = 350,
	AIS_DAC_351_PANAMA = 351,
	AIS_DAC_352_PANAMA = 352,
	AIS_DAC_353_PANAMA = 353,
	AIS_DAC_354_PANAMA = 354,
	AIS_DAC_355_PANAMA = 355,
	AIS_DAC_356_PANAMA = 356,
	AIS_DAC_357_PANAMA = 357,
	AIS_DAC_358_PUERTO_RICO = 358,
	AIS_DAC_359_EL_SALVADOR = 359,
	AIS_DAC_361_SAINT_PIERRE_AND_MIQUELON = 361,
	AIS_DAC_362_TRINIDAD_AND_TOBAGO = 362,
	AIS_DAC_364_TURKS_AND_CAICOS_ISLANDS = 364,
	AIS_DAC_366_UNITED_STATES_OF_AMERICA = 366,
	AIS_DAC_367_UNITED_STATES_OF_AMERICA = 367,
	AIS_DAC_368_UNITED_STATES_OF_AMERICA = 368,
	AIS_DAC_369_UNITED_STATES_OF_AMERICA = 369,
	AIS_DAC_370_PANAMA = 370,
	AIS_DAC_371_PANAMA = 371,
	AIS_DAC_372_PANAMA = 372,
	AIS_DAC_373_PANAMA = 373,
	AIS_DAC_375_SAINT_VINCENT_AND_THE_GRENADINES = 375,
	AIS_DAC_376_SAINT_VINCENT_AND_THE_GRENADINES = 376,
	AIS_DAC_377_SAINT_VINCENT_AND_THE_GRENADINES = 377,
	AIS_DAC_378_BRITISH_VIRGIN_ISLANDS = 378,
	AIS_DAC_379_UNITED_STATES_VIRGIN_ISLANDS = 379,
	AIS_DAC_401_AFGHANISTAN = 401,
	AIS_DAC_403_SAUDI_ARABIA = 403,
	AIS_DAC_405_BANGLADESH = 405,
	AIS_DAC_408_BAHRAIN = 408,
	AIS_DAC_410_BHUTAN = 410,
	AIS_DAC_412_CHINA = 412,
	AIS_DAC_413_CHINA = 413,
	AIS_DAC_414_CHINA = 414,
	AIS_DAC_416_TAIWAN = 416,
	AIS_DAC_417_SRI_LANKA = 417,
	AIS_DAC_419_INDIA = 419,
	AIS_DAC_422_IRAN = 422,
	AIS_DAC_423_AZERBAIJAN = 423,
	AIS_DAC_425_IRAQ = 425,
	AIS_DAC_428_ISRAEL = 428,
	AIS_DAC_431_JAPAN = 431,
	AIS_DAC_432_JAPAN = 432,
	AIS_DAC_434_TURKMENISTAN = 434,
	AIS_DAC_436_KAZAKHSTAN = 436,
	AIS_DAC_437_UZBEKISTAN = 437,
	AIS_DAC_438_JORDAN = 438,
	AIS_DAC_440_KOREA = 440,
	AIS_DAC_441_KOREA = 441,
	AIS_DAC_443_STATE_OF_PALESTINE = 443,
	AIS_DAC_445_DEMOCRATIC_PEOPLES_REPUBLIC_OF_KOREA = 445,
	AIS_DAC_447_KUWAIT = 447,
	AIS_DAC_450_LEBANON = 450,
	AIS_DAC_451_KYRGYZ_REPUBLIC = 451,
	AIS_DAC_453_MACAO = 453,
	AIS_DAC_455_MALDIVES = 455,
	AIS_DAC_457_MONGOLIA = 457,
	AIS_DAC_459_NEPAL = 459,
	AIS_DAC_461_OMAN = 461,
	AIS_DAC_463_PAKISTAN = 463,
	AIS_DAC_466_QATAR = 466,
	AIS_DAC_468_SYRIAN_ARAB_REPUBLIC = 468,
	AIS_DAC_470_UNITED_ARAB_EMIRATES = 470,
	AIS_DAC_472_TAJIKISTAN = 472,
	AIS_DAC_473_YEMEN = 473,
	AIS_DAC_475_YEMEN = 475,
	AIS_DAC_477_HONG_KONG = 477,
	AIS_DAC_478_BOSNIA_AND_HERZEGOVINA = 478,
	AIS_DAC_501_ADELIE_LAND = 501,
	AIS_DAC_503_AUSTRALIA = 503,
	AIS_DAC_506_MYANMAR = 506,
	AIS_DAC_508_BRUNEI_DARUSSALAM = 508,
	AIS_DAC_510_MICRONESIA = 510,
	AIS_DAC_511_PALAU = 511,
	AIS_DAC_512_NEW_ZEALAND = 512,
	AIS_DAC_514_CAMBODIA = 514,
	AIS_DAC_515_CAMBODIA = 515,
	AIS_DAC_516_CHRISTMAS_ISLAND = 516,
	AIS_DAC_518_COOK_ISLANDS = 518,
	AIS_DAC_520_FIJI = 520,
	AIS_DAC_523_COCOS = 523,
	AIS_DAC_525_INDONESIA = 525,
	AIS_DAC_529_KIRIBATI = 529,
	AIS_DAC_531_LAO_PEOPLES_DEMOCRATIC_REPUBLIC = 531,
	AIS_DAC_533_MALAYSIA = 533,
	AIS_DAC_536_NORTHERN_MARIANA_ISLANDS = 536,
	AIS_DAC_538_MARSHALL_ISLANDS = 538,
	AIS_DAC_540_NEW_CALEDONIA = 540,
	AIS_DAC_542_NIUE = 542,
	AIS_DAC_544_NAURU = 544,
	AIS_DAC_546_FRENCH_POLYNESIA = 546,
	AIS_DAC_548_PHILIPPINES = 548,
	AIS_DAC_553_PAPUA_NEW_GUINEA = 553,
	AIS_DAC_555_PITCAIRN_ISLAND = 555,
	AIS_DAC_557_SOLOMON_ISLANDS = 557,
	AIS_DAC_559_AMERICAN_SAMOA = 559,
	AIS_DAC_561_SAMOA = 561,
	AIS_DAC_563_SINGAPORE = 563,
	AIS_DAC_564_SINGAPORE = 564,
	AIS_DAC_565_SINGAPORE = 565,
	AIS_DAC_566_SINGAPORE = 566,
	AIS_DAC_567_THAILAND = 567,
	AIS_DAC_570_TONGA = 570,
	AIS_DAC_572_TUVALU = 572,
	AIS_DAC_574_VIETNAM = 574,
	AIS_DAC_576_VANUATU = 576,
	AIS_DAC_577_VANUATU = 577,
	AIS_DAC_578_WALLIS_AND_FUTUNA_ISLANDS = 578,
	AIS_DAC_601_SOUTH_AFRICA = 601,
	AIS_DAC_603_ANGOLA = 603,
	AIS_DAC_605_ALGERIA = 605,
	AIS_DAC_607_SAINT_PAUL_AND_AMSTERDAM_ISLANDS = 607,
	AIS_DAC_608_ASCENSION_ISLAND = 608,
	AIS_DAC_609_BURUNDI = 609,
	AIS_DAC_610_BENIN = 610,
	AIS_DAC_611_BOTSWANA = 611,
	AIS_DAC_612_CENTRAL_AFRICAN_REPUBLIC = 612,
	AIS_DAC_613_CAMEROON = 613,
	AIS_DAC_615_CONGO = 615,
	AIS_DAC_616_COMOROS = 616,
	AIS_DAC_617_CABO_VERDE = 617,
	AIS_DAC_618_CROZET_ARCHIPELAGO = 618,
	AIS_DAC_619_COTE_DIVOIRE = 619,
	AIS_DAC_620_COMOROS = 620,
	AIS_DAC_621_DJIBOUTI = 621,
	AIS_DAC_622_EGYPT = 622,
	AIS_DAC_624_ETHIOPIA = 624,
	AIS_DAC_625_ERITREA = 625,
	AIS_DAC_626_GABONESE_REPUBLIC = 626,
	AIS_DAC_627_GHANA = 627,
	AIS_DAC_629_GAMBIA = 629,
	AIS_DAC_630_GUINEABISSAU = 630,
	AIS_DAC_631_EQUATORIAL_GUINEA = 631,
	AIS_DAC_632_GUINEA = 632,
	AIS_DAC_633_BURKINA_FASO = 633,
	AIS_DAC_634_KENYA = 634,
	AIS_DAC_635_KERGUELEN_ISLANDS = 635,
	AIS_DAC_636_LIBERIA = 636,
	AIS_DAC_637_LIBERIA = 637,
	AIS_DAC_638_SOUTH_SUDAN = 638,
	AIS_DAC_642_LIBYA = 642,
	AIS_DAC_644_LESOTHO = 644,
	AIS_DAC_645_MAURITIUS = 645,
	AIS_DAC_647_MADAGASCAR = 647,
	AIS_DAC_649_MALI = 649,
	AIS_DAC_650_MOZAMBIQUE = 650,
	AIS_DAC_654_MAURITANIA = 654,
	AIS_DAC_655_MALAWI = 655,
	AIS_DAC_656_NIGER = 656,
	AIS_DAC_657_NIGERIA = 657,
	AIS_DAC_659_NAMIBIA = 659,
	AIS_DAC_660_REUNION = 660,
	AIS_DAC_661_RWANDA = 661,
	AIS_DAC_662_SUDAN = 662,
	AIS_DAC_663_SENEGAL = 663,
	AIS_DAC_664_SEYCHELLES = 664,
	AIS_DAC_665_SAINT_HELENA = 665,
	AIS_DAC_666_SOMALIA = 666,
	AIS_DAC_667_SIERRA_LEONE = 667,
	AIS_DAC_668_SAO_TOME_AND_PRINCIPE = 668,
	AIS_DAC_669_SWAZILAND = 669,
	AIS_DAC_670_CHAD = 670,
	AIS_DAC_671_TOGOLESE_REPUBLIC = 671,
	AIS_DAC_672_TUNISIA = 672,
	AIS_DAC_674_TANZANIA = 674,
	AIS_DAC_675_UGANDA = 675,
	AIS_DAC_676_DEMOCRATIC_REPUBLIC_OF_THE_CONGO = 676,
	AIS_DAC_677_TANZANIA = 677,
	AIS_DAC_678_ZAMBIA = 678,
	AIS_DAC_679_ZIMBABWE = 679,
	AIS_DAC_701_ARGENTINE_REPUBLIC = 701,
	AIS_DAC_710_BRAZIL = 710,
	AIS_DAC_720_BOLIVIA = 720,
	AIS_DAC_725_CHILE = 725,
	AIS_DAC_730_COLOMBIA = 730,
	AIS_DAC_735_ECUADOR = 735,
	AIS_DAC_740_FALKLAND_ISLANDS = 740,
	AIS_DAC_745_GUIANA = 745,
	AIS_DAC_750_GUYANA = 750,
	AIS_DAC_755_PARAGUAY = 755,
	AIS_DAC_760_PERU = 760,
	AIS_DAC_765_SURINAME = 765,
	AIS_DAC_770_URUGUAY = 770,
	AIS_DAC_775_VENEZUELA = 775
};

class AisPoint {
public:
	double lng_deg;
	double lat_deg;

	AisPoint();
	AisPoint(double lng_deg_, double lat_deg_);
};
ostream& operator<< (ostream &o, const AisPoint &position);

//////////////////////////////////////////////////////////////////////
// Support class for decoding
//////////////////////////////////////////////////////////////////////
static const int MAX_BITS = 1192;

class AisBitset : protected bitset<MAX_BITS> {
public:
	AisBitset();

	AIS_STATUS ParseNmeaPayload(const char *nmea_payload, int pad);

	int GetNumBits() const { return num_bits; }
	int GetNumChars() const { return num_chars; }
	int GetPosition() const { return current_position; }
	int GetRemaining() const { return num_bits - current_position; }

	const AisBitset& SeekRelative(int d) const;
	const AisBitset& SeekTo(size_t pos) const;

	bool operator[](size_t pos) const;

	unsigned int ToUnsignedInt(const size_t start, const size_t len) const;
	int ToInt(const size_t start, const size_t len) const;
	string ToString(const size_t start, const size_t len) const;

	const AisPoint ToAisPoint(const size_t start, const size_t point_size) const;

	// Visible for testing.
	static bitset<6> Reverse(const bitset<6> &bits);

protected:
	int num_bits;
	int num_chars;

	static bool nmea_ord_initialized_;
	static bitset<6> nmea_ord_[128];
	static const char bits_to_char_tbl_[];

	static void InitNmeaOrd();

private:
	// This will help uncover dicontinuities when querying sequential bits, i.e.
	// when we query a bit sequence that is not in direct succession of the
	// previous one. In the future, we may use this to automatically determine
	// the next read location.
	// This field is also used to determine the number of remaining bits after the
	// last read position.
	// That being said, the 'start' argument in all the 'To...' methods above is
	// redundant and the only purpose is to discover typos in the bit positions in
	// each message's parse method, i.e. debugging.
	mutable int current_position;
};

template <int size>
inline std::string arrayinttostring(const std::array<int,size> & a)
{
	std::string res;
	for(auto v:a)
	{
		char buf[64];
		snprintf(buf,64,"%d",v);
		if (res.size())
			res+=",";
		res+= buf;
	}
	return "["+res+"]";
}
template <int size>
inline std::string arrayfloattostring(const std::array<float,size> & a)
{
	std::string res;
	for(auto v:a)
	{
		char buf[64];
		snprintf(buf,64,"%.9f",v);
		if (res.size())
			res+=",";
		res+= buf;
	}
	return "["+res+"]";
}
inline  std::string intarraytostring(const int * v,int size)
{
	std::string res;
	for(int i=0;i<size;++i)
	{
		char buf[64];
		snprintf(buf,64,"%d",v[i]);
		if (res.size())
			res+=",";
		res+= buf;
	}
	return "["+res+"]";
}
inline  std::string vectorinttostring(const std::vector<int> & a)
{
	std::string res;
	for(auto v:a)
	{
		char buf[64];
		snprintf(buf,64,"%d",v);
		if (res.size())
			res+=",";
		res+= buf;
	}
	return "["+res+"]";
}
inline  std::string vectorfloattostring(const std::vector<float> & a)
{
	std::string res;
	for(auto v:a)
	{
		char buf[64];
		snprintf(buf,64,"%.9f",v);
		if (res.size())
			res+=",";
		res+= buf;
	}
	return "["+res+"]";
}

inline std::string int2string(int v) {
	char buf[64];
	snprintf(buf,64,"%d",v);
	return buf;
};
inline std::string bool2string(bool v) {
	char buf[64];
	snprintf(buf,64,"%d",v?1:0);
	return buf;
};
inline std::string float2string(float v) {
	char buf[64];
	snprintf(buf,64,"%.9f",v);
	return buf;
};
inline std::string double2string(double v) {
	char buf[64];
	snprintf(buf,64,"%.16lf",v);
	return buf;
};
inline std::string string2string(const std::string & v) {
	return v;
};
inline std::string unsignedint2string(unsigned int  v) {
	char buf[64];
	snprintf(buf,64,"%u",v);
	return buf;
};
inline void AisPoint2string(AisPoint v, std::string prefix, std::map<std::string,std::string>&res) {
	char buf[64];
	snprintf(buf,64,"%.16f",v.lat_deg);
	res[prefix+"_lat"] = buf;
	snprintf(buf,64,"%.16f",v.lng_deg);
	res[prefix+"_lon"] = buf;
};

class AisMsg {
public:


public:
	int message_id = 0;
	int repeat_indicator = 0;
	int mmsi = 0;

	// TODO(schwehr): make status private and have accessors.
	bool had_error() const {  return status != AIS_OK;  }
	AIS_STATUS get_error() const { return status; }
	virtual ~AisMsg() {}
	virtual std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const
	{
		std::map<std::string,std::string> res(std::move(super));
		res["message_id"] = int2string(message_id);
		res["repeat_indicator"] = int2string(repeat_indicator);
		res["mmsi"] = int2string(mmsi);
		return res;
	}
protected:
	AIS_STATUS status;  // AIS_OK or error code
	int num_chars;  // Number of characters in the nmea_payload.
	size_t num_bits;  // Number of bits in the nmea_payload.
	AisBitset bits;  // The bitset that was constructed out of the nmea_payload.

	AisMsg() : status(AIS_UNINITIALIZED), num_chars(0), num_bits(0), bits() {}
	AisMsg(const char *nmea_payload, const size_t pad);

	// Returns true if the msg is in a good state "so far", i.e. either AIS_OK or
	// AIS_UNINITIALIZED.
	bool CheckStatus() const;
};

// TODO(schwehr): factor out commstate from all messages?
class Ais1_2_3 : public AisMsg {
public:
	int nav_status;
	bool rot_over_range;
	int rot_raw;
	float rot;
	float sog;  // Knots.
	int position_accuracy;
	AisPoint position;
	float cog;  // Degrees.
	int true_heading;
	// TODO(schwehr): What about a leap second when timestamp 60 may be valid?
	int timestamp;
	int special_manoeuvre;
	int spare;
	bool raim;

	// COMM state SOTDMA msgs 1 and 2
	int sync_state;  // SOTDMA and ITDMA
	bool slot_timeout_valid;
	int slot_timeout;

	// Based on slot_timeout which ones are valid
	bool received_stations_valid;
	int received_stations;

	bool slot_number_valid;
	int slot_number;

	bool utc_valid;
	int utc_hour;
	int utc_min;
	int utc_spare;

	bool slot_offset_valid;
	int slot_offset;

	// ITDMA - msg type 3
	bool slot_increment_valid;
	int slot_increment;

	bool slots_to_allocate_valid;
	int slots_to_allocate;

	bool keep_flag_valid;
	bool keep_flag;  // 3.3.7.3.2 Annex 2 ITDMA.  Table 20

	Ais1_2_3(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["nav_status"]=int2string(nav_status);
		res["rot_over_range"]=bool2string(rot_over_range);
		res["rot_raw"]=int2string(rot_raw);
		res["rot"]=float2string(rot);
		res["sog"]=float2string(sog);
		res["position_accuracy"]=int2string(position_accuracy);
		AisPoint2string(position,"position",res);
		res["cog"]=float2string(cog);
		res["true_heading"]=int2string(true_heading);
		res["timestamp"]=int2string(timestamp);
		res["special_manoeuvre"]=int2string(special_manoeuvre);
		res["spare"]=int2string(spare);
		res["raim"]=bool2string(raim);
		res["sync_state"]=int2string(sync_state);
		res["slot_timeout_valid"]=bool2string(slot_timeout_valid);
		res["slot_timeout"]=int2string(slot_timeout);
		res["received_stations_valid"]=bool2string(received_stations_valid);
		res["received_stations"]=int2string(received_stations);
		res["slot_number_valid"]=bool2string(slot_number_valid);
		res["slot_number"]=int2string(slot_number);
		res["utc_valid"]=bool2string(utc_valid);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["utc_spare"]=int2string(utc_spare);
		res["slot_offset_valid"]=bool2string(slot_offset_valid);
		res["slot_offset"]=int2string(slot_offset);
		res["slot_increment_valid"]=bool2string(slot_increment_valid);
		res["slot_increment"]=int2string(slot_increment);
		res["slots_to_allocate_valid"]=bool2string(slots_to_allocate_valid);
		res["slots_to_allocate"]=int2string(slots_to_allocate);
		res["keep_flag_valid"]=bool2string(keep_flag_valid);
		res["keep_flag"]=bool2string(keep_flag);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais1_2_3 &msg);

// 4 bsreport and 11 utc date response
class Ais4_11 : public AisMsg {
public:
	int year;
	int month;
	int day;
	int hour;
	int minute;
	int second;
	int position_accuracy;
	AisPoint position;
	int fix_type;
	int transmission_ctl;
	int spare;
	bool raim;

	// COMM state SOTDMA msgs 1 and 2
	int sync_state;
	int slot_timeout;

	// Based on slot_timeout which ones are valid
	bool received_stations_valid;
	int received_stations;

	bool slot_number_valid;
	int slot_number;

	bool utc_valid;
	int utc_hour;
	int utc_min;
	int utc_spare;

	bool slot_offset_valid;
	int slot_offset;

	// **NO** ITDMA
	Ais4_11(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["year"]=int2string(year);
		res["month"]=int2string(month);
		res["day"]=int2string(day);
		res["hour"]=int2string(hour);
		res["minute"]=int2string(minute);
		res["second"]=int2string(second);
		res["position_accuracy"]=int2string(position_accuracy);
		AisPoint2string(position,"position",res);
		res["fix_type"]=int2string(fix_type);
		res["transmission_ctl"]=int2string(transmission_ctl);
		res["spare"]=int2string(spare);
		res["raim"]=bool2string(raim);
		res["sync_state"]=int2string(sync_state);
		res["slot_timeout"]=int2string(slot_timeout);
		res["received_stations_valid"]=bool2string(received_stations_valid);
		res["received_stations"]=int2string(received_stations);
		res["slot_number_valid"]=bool2string(slot_number_valid);
		res["slot_number"]=int2string(slot_number);
		res["utc_valid"]=bool2string(utc_valid);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["utc_spare"]=int2string(utc_spare);
		res["slot_offset_valid"]=bool2string(slot_offset_valid);
		res["slot_offset"]=int2string(slot_offset);
		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais4_11 &msg);

class Ais5 : public AisMsg {
public:
	int ais_version;
	int imo_num;
	string callsign;
	string name;
	int type_and_cargo;
	int dim_a;
	int dim_b;
	int dim_c;
	int dim_d;
	int fix_type;
	int eta_month;
	int eta_day;
	int eta_hour;
	int eta_minute;
	float draught;  // present static draft. m
	string destination;
	int dte;
	int spare;

	Ais5(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["ais_version"]=int2string(ais_version);
		res["imo_num"]=int2string(imo_num);
		res["callsign"]=string2string(callsign);
		res["name"]=string2string(name);
		res["type_and_cargo"]=int2string(type_and_cargo);
		res["dim_a"]=int2string(dim_a);
		res["dim_b"]=int2string(dim_b);
		res["dim_c"]=int2string(dim_c);
		res["dim_d"]=int2string(dim_d);
		res["fix_type"]=int2string(fix_type);
		res["eta_month"]=int2string(eta_month);
		res["eta_day"]=int2string(eta_day);
		res["eta_hour"]=int2string(eta_hour);
		res["eta_minute"]=int2string(eta_minute);
		res["draught"]=float2string(draught);
		res["destination"]=string2string(destination);
		res["dte"]=int2string(dte);
		res["spare"]=int2string(spare);
		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais5 &msg);

// AIS Binary Broadcast message ... parent to many
class Ais6 : public AisMsg {
public:
	int seq;  // sequence number
	int mmsi_dest;
	bool retransmit;
	int spare;
	int dac;  // dac+fi = app id
	int fi;

	// TODO(schwehr): how to make Ais6 protected?
	Ais6(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["seq"]=int2string(seq);
		res["mmsi_dest"]=int2string(mmsi_dest);
		res["retransmit"]=bool2string(retransmit);
		res["spare"]=int2string(spare);
		res["dac"]=int2string(dac);
		res["fi"]=int2string(fi);

		return AisMsg::values(res);
	}
protected:
	Ais6() {}
};
ostream& operator<< (ostream &o, const Ais6 &msg);

// http://www.e-navigation.nl/content/monitoring-aids-navigation
// Zeni Lite Buoy Co., Ltd buoy status.
class Ais6_0_0 : public Ais6 {
public:
	int sub_id;
	float voltage;
	float current;
	bool dc_power_supply;  // False is AC.
	bool light_on;
	bool battery_low;
	bool off_position;
	int spare2;

	Ais6_0_0(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["sub_id"]=int2string(sub_id);
		res["voltage"]=float2string(voltage);
		res["current"]=float2string(current);
		res["dc_power_supply"]=bool2string(dc_power_supply);
		res["light_on"]=bool2string(light_on);
		res["battery_low"]=bool2string(battery_low);
		res["off_position"]=bool2string(off_position);
		res["spare2"]=int2string(spare2);
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_0_0 &msg);

// Text message.  ITU 1371-1
class Ais6_1_0 : public Ais6 {
public:
	bool ack_required;
	int msg_seq;
	string text;
	int spare2;

	Ais6_1_0(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["ack_required"]=bool2string(ack_required);
		res["msg_seq"]=int2string(msg_seq);
		res["text"]=string2string(text);
		res["spare2"]=int2string(spare2);

		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_0 &msg);

// Application ack.  ITU 1371-1
class Ais6_1_1 : public Ais6 {
public:
	int ack_dac;
	int msg_seq;
	int spare2;

	Ais6_1_1(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["ack_dac"]=int2string(ack_dac);
		res["msg_seq"]=int2string(msg_seq);
		res["spare2"]=int2string(spare2);

		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_1 &msg);

// Interrogation for a DAC/FI.  ITU 1371-1
class Ais6_1_2 : public Ais6 {
public:
	int req_dac;
	int req_fi;
	// TODO(schwehr): spare2?

	Ais6_1_2(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["req_dac"]=int2string(req_dac);
		res["req_fi"]=int2string(req_fi);
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_2 &msg);

// Capability interogation.  ITU 1371-1
class Ais6_1_3 : public Ais6 {
public:
	unsigned int req_dac;
	unsigned int spare2;
	unsigned int spare3;
	unsigned int spare4;

	Ais6_1_3(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["req_dac"]=unsignedint2string(req_dac);
		res["spare2"]=unsignedint2string(spare2);
		res["spare3"]=unsignedint2string(spare3);
		res["spare4"]=unsignedint2string(spare4);
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_3 &msg);

// Capability interogation reply.  ITU 1371-1
// 5.4 International function message 4: Capability reply
class Ais6_1_4 : public Ais6 {
public:
	int ack_dac;
	std::array<int, 64> capabilities;
	std::array<int, 64> cap_reserved;
	// 126 bits of spare.  So dumb.
	int spare2;
	int spare3;
	int spare4;
	int spare5;

	Ais6_1_4(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["ack_dac"]=unsignedint2string(ack_dac);
		res["spare2"]=unsignedint2string(spare2);
		res["spare3"]=unsignedint2string(spare3);
		res["spare4"]=unsignedint2string(spare4);
		res["spare5"]=unsignedint2string(spare5);
		res["capabilities"]=arrayinttostring<64>(capabilities);
		res["cap_reserved"]=arrayinttostring<64>(cap_reserved);
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_4 &msg);

// International function message 5: Application ack to addr binary message.
class Ais6_1_5 : public Ais6 {
public:
	// TODO(schwehr): How to handle the sequence number and retransmit flag?
	int ack_dac;
	int ack_fi;
	int seq_num;
	bool ai_available;  // TODO(schwehr): AI?  Is this the dac/fi being acked?
	int ai_response;
	int spare;
	int spare2;

	Ais6_1_5(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["ack_dac"]=int2string(ack_dac);
		res["ack_fi"]=int2string(ack_fi);
		res["seq_num"]=int2string(seq_num);
		res["ai_available"]=bool2string(ai_available);
		res["ai_response"]=int2string(ai_response);
		res["spare"]=int2string(spare);
		res["spare2"]=int2string(spare2);
		return Ais6::values(res);
	}
};

// IMO Circ 236 Dangerous cargo indication
// Not to be transmitted after 2012-Jan-01
class Ais6_1_12 : public Ais6 {
public:
	string last_port;
	int utc_month_dep;  // actual time of departure
	int utc_day_dep;
	int utc_hour_dep;
	int utc_min_dep;
	string next_port;
	int utc_month_next;  // estimated arrival
	int utc_day_next;
	int utc_hour_next;
	int utc_min_next;
	string main_danger;
	string imo_cat;
	int un;
	int value;  // TODO(schwehr): units?
	int value_unit;
	int spare2;

	Ais6_1_12(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["last_port"]=string2string(last_port);
		res["utc_month_dep"]=int2string(utc_month_dep);
		res["utc_day_dep"]=int2string(utc_day_dep);
		res["utc_hour_dep"]=int2string(utc_hour_dep);
		res["utc_min_dep"]=int2string(utc_min_dep);
		res["next_port"]=string2string(next_port);
		res["utc_month_next"]=int2string(utc_month_next);
		res["utc_day_next"]=int2string(utc_day_next);
		res["utc_hour_next"]=int2string(utc_hour_next);
		res["utc_min_next"]=int2string(utc_min_next);
		res["main_danger"]=string2string(main_danger);
		res["imo_cat"]=string2string(imo_cat);
		res["un"]=int2string(un);
		res["value"]=int2string(value);
		res["value_unit"]=int2string(value_unit);
		res["spare2"]=int2string(spare2);
		return Ais6::values(res);
	}

};
ostream& operator<< (ostream &o, const Ais6_1_12 &msg);


class Ais6_1_14_Window {
public:
	AisPoint position;
	int utc_hour_from;
	int utc_min_from;
	int utc_hour_to;
	int utc_min_to;
	int cur_dir;
	float cur_speed;
};


// IMO Circ 236 Tidal window
// Not to be transmitted after 2012-Jan-01
class Ais6_1_14 : public Ais6 {
public:
	int utc_month;
	int utc_day;
	vector<Ais6_1_14_Window> windows;

	Ais6_1_14(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["utc_month"]=int2string(utc_month);
		res["utc_day"]=int2string(utc_day);
		int c = 0;
		for (auto v:windows)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"windows%d_",c);
			std::string key = bufkey;
			++c;
			AisPoint2string(v.position,key+"position",res);
			res[key+"utc_hour_from"]=int2string(v.utc_hour_from);
			res[key+"utc_min_from"]=int2string(v.utc_min_from);
			res[key+"utc_hour_to"]=int2string(v.utc_hour_to);
			res[key+"utc_min_to"]=int2string(v.utc_min_to);
			res[key+"cur_dir"]=int2string(v.cur_dir);
			res[key+"cur_speed"]=float2string(v.cur_speed);
		}
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_14 &msg);


// IMO Circ 289 Clearance time to enter port
class Ais6_1_18 : public Ais6 {
public:
	int link_id;
	int utc_month;
	int utc_day;
	int utc_hour;
	int utc_min;
	string port_berth;
	string dest;
	AisPoint position;
	std::array<int, 2> spare2;  // 32 bits per spare

	Ais6_1_18(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["utc_month"]=int2string(utc_month);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["port_berth"]=string2string(port_berth);
		res["dest"]=string2string(dest);
		AisPoint2string(position,"position",res);
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_18 &msg);


// IMO Circ 289 Berthing data
class Ais6_1_20 : public Ais6 {
public:
	int link_id;
	int length;
	float depth;
	int mooring_position;
	int utc_month;
	int utc_day;
	int utc_hour;
	int utc_min;
	bool services_known;
	// TODO(schwehr): enum of service types
	std::array<int, 26> services;
	string name;
	AisPoint position;

	Ais6_1_20(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["length"]=int2string(length);
		res["depth"]=float2string(depth);
		res["mooring_position"]=int2string(mooring_position);
		res["utc_month"]=int2string(utc_month);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["services_known"]=bool2string(services_known);
		res["name"]=string2string(name);
		AisPoint2string(position,"position",res);
		res["services"]=arrayinttostring<26>(services);
		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_20 &msg);

class Ais6_1_25_Cargo {
public:
	int code_type;
	bool imdg_valid;  // also set with BC
	int imdg;
	bool spare_valid;
	int spare;  // imdg or dc or marpols
	bool un_valid;
	int un;
	bool bc_valid;
	int bc;
	bool marpol_oil_valid;
	int marpol_oil;
	bool marpol_cat_valid;
	int marpol_cat;

	Ais6_1_25_Cargo();
	// TODO(schwehr): Add a constructor from an AisBitset.

};

// IMO Circ 289 Dangerous cargo indication 2
// Replaces 8_1_12?
class Ais6_1_25 : public Ais6 {
public:
	int amount_unit;
	int amount;

	vector<Ais6_1_25_Cargo> cargos;  // 0 to 17 cargo entries

	Ais6_1_25(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["amount_unit"]=int2string(amount_unit);
		res["amount"]=int2string(amount);
		int c = 0;
		for (auto v:cargos)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"cargos%d_",c);
			std::string key = bufkey;
			++c;
			res[key+"code_type"]=int2string(v.code_type);
			res[key+"imdg_valid"]=bool2string(v.imdg_valid);
			res[key+"imdg"]=int2string(v.imdg);
			res[key+"spare_valid"]=bool2string(v.spare_valid);
			res[key+"spare"]=int2string(v.spare);
			res[key+"un_valid"]=bool2string(v.un_valid);
			res[key+"un"]=int2string(v.un);
			res[key+"bc_valid"]=bool2string(v.bc_valid);
			res[key+"bc"]=int2string(v.bc);
			res[key+"marpol_oil_valid"]=bool2string(v.marpol_oil_valid);
			res[key+"marpol_oil"]=int2string(v.marpol_oil);
			res[key+"marpol_cat_valid"]=bool2string(v.marpol_cat_valid);
			res[key+"marpol_cat"]=int2string(v.marpol_cat);
		}

		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_25 &msg);

// TODO(schwehr): Addressed sensor report 6_1_26.
// TODO(schwehr): IMO Circ 289 Route information 6_1_28.
// TODO(schwehr): IMO Circ 289 Text description 6_1_30.

// IMO Circ 289
// Warning: The bit encoding for 6_1_14_Window and 6_1_32 on
//   the wire has x and y in a different order.
// TODO(schwehr): Reuse Ais6_1_14_Window
class Ais6_1_32_Window {
public:
	AisPoint position;
	int from_utc_hour;
	int from_utc_min;
	int to_utc_hour;
	int to_utc_min;
	int cur_dir;
	float cur_speed;  // knots

	Ais6_1_32_Window();
};


// IMO Circ 289 Tidal window
class Ais6_1_32 : public Ais6 {
public:
	int utc_month;
	int utc_day;
	vector<Ais6_1_32_Window> windows;

	Ais6_1_32(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["utc_month"]=int2string(utc_month);
		res["utc_day"]=int2string(utc_day);
		int c = 0;
		for (auto v:windows)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"windows%d_",c);
			std::string key = bufkey;
			++c;
			AisPoint2string(v.position,key+"position",res);
			res[key+"from_utc_hour"]=int2string(v.from_utc_hour);
			res[key+"from_utc_min"]=int2string(v.from_utc_min);
			res[key+"to_utc_hour"]=int2string(v.to_utc_hour);
			res[key+"to_utc_min"]=int2string(v.to_utc_min);
			res[key+"cur_dir"]=int2string(v.cur_dir);
			res[key+"cur_speed"]=float2string(v.cur_speed);

		}

		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_32 &msg);

// Number of persons on board.  ITU 1371-1
class Ais6_1_40 : public Ais6 {
public:
	int persons;
	int spare2;

	Ais6_1_40(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(persons);
		res["utc_month"]=int2string(persons);

		return Ais6::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais6_1_40 &msg);

//////////////////////////////////////////////////////////////////////

// 7 and 13 are ACKs for msg 6 and 12
class Ais7_13 : public AisMsg {
public:
	int spare;

	vector<int> dest_mmsi;
	vector<int> seq_num;

	Ais7_13(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["dest_mmsi"]=vectorinttostring(dest_mmsi);
		res["seq_num"]=vectorinttostring(seq_num);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais7_13 &msg);

// AIS Binary Broadcast message ... parent to many
class Ais8 : public AisMsg {
public:
	int spare;
	// TODO(schwehr): seq? // ITU M.R. 1371-3 Anex 2 5.3.1
	int dac;  // dac+fi = app id
	int fi;

	// TODO(schwehr): make Ais8 protected
	Ais8(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["dac"]=int2string(dac);
		res["fi"]=int2string(fi);

		return AisMsg::values(res);
	}

protected:
	Ais8() {}
};
ostream& operator<< (ostream &o, const Ais8 &msg);

// Text telegram ITU 1371-1
class Ais8_1_0 : public Ais8 {
public:
	bool ack_required;
	int msg_seq;
	string text;
	int spare2;

	Ais8_1_0(const char *nmea_payload, size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["ack_required"]=bool2string(ack_required);
		res["msg_seq"]=int2string(msg_seq);
		res["text"]=string2string(text);
		res["spare2"]=int2string(spare2);

		return Ais8::values(res);
	}

};
ostream& operator<< (ostream &o, const Ais8_1_0 &msg);

// 8_1_1 No message
// 8_1_2 No message
// 8_1_3 No message
// 8_1_4 No message

// IMO Circ 289 met hydro - Not to be transmitted after 2013-Jan-01
// See also IMO Circ 236
class Ais8_1_11 : public Ais8 {
public:
	AisPoint position;
	int day;
	int hour;
	int minute;
	int wind_ave;  // kts
	int wind_gust;  // kts
	int wind_dir;
	int wind_gust_dir;
	float air_temp;  // C
	int rel_humid;
	float dew_point;
	float air_pres;
	int air_pres_trend;
	float horz_vis;  // NM
	float water_level;  // m
	int water_level_trend;
	float surf_cur_speed;
	int surf_cur_dir;
	float cur_speed_2;  // kts
	int cur_dir_2;
	int cur_depth_2;  // m
	float cur_speed_3;  // kts
	int cur_dir_3;
	int cur_depth_3;  // m
	float wave_height;  // m
	int wave_period;
	int wave_dir;
	float swell_height;
	int swell_period;
	int swell_dir;
	int sea_state;  // beaufort scale
	float water_temp;
	int precip_type;
	float salinity;  // Part per mil (1/1000).
	int ice;  // yes/no/undef/unknown
	int spare2;
	int extended_water_level;  // OHMEX uses this for extra water level precision

	Ais8_1_11(const char *nmea_payload, size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,"position",res);
		res["day"]=int2string(day);
		res["hour"]=int2string(hour);
		res["minute"]=int2string(minute);
		res["wind_ave"]=int2string(wind_ave);
		res["wind_gust"]=int2string(wind_gust);
		res["wind_dir"]=int2string(wind_dir);
		res["wind_gust_dir"]=int2string(wind_gust_dir);
		res["air_temp"]=float2string(air_temp);
		res["rel_humid"]=int2string(rel_humid);
		res["dew_point"]=float2string(dew_point);
		res["air_pres"]=float2string(air_pres);
		res["air_pres_trend"]=int2string(air_pres_trend);
		res["horz_vis"]=float2string(horz_vis);
		res["water_level"]=float2string(water_level);
		res["water_level_trend"]=int2string(water_level_trend);
		res["surf_cur_speed"]=float2string(surf_cur_speed);
		res["surf_cur_dir"]=int2string(surf_cur_dir);
		res["cur_speed_2"]=float2string(cur_speed_2);
		res["cur_dir_2"]=int2string(cur_dir_2);
		res["cur_depth_2"]=int2string(cur_depth_2);
		res["cur_speed_3"]=float2string(cur_speed_3);
		res["cur_dir_3"]=int2string(cur_dir_3);
		res["cur_depth_3"]=int2string(cur_depth_3);
		res["wave_height"]=float2string(wave_height);
		res["wave_period"]=int2string(wave_period);
		res["wave_dir"]=int2string(wave_dir);
		res["swell_height"]=float2string(swell_height);
		res["swell_period"]=int2string(swell_period);
		res["swell_dir"]=int2string(swell_dir);
		res["sea_state"]=int2string(sea_state);
		res["water_temp"]=float2string(water_temp);
		res["precip_type"]=int2string(precip_type);
		res["salinity"]=float2string(salinity);
		res["ice"]=int2string(ice);
		res["spare2"]=int2string(spare2);
		res["extended_water_level"]=int2string(extended_water_level);

		return Ais8::values(res);
	}

};
ostream& operator<< (ostream &o, const Ais8_1_11 &msg);


// IMO Circ 236 Fairway closed - Not to be transmitted after 2012-Jan-01
class Ais8_1_13 : public Ais8 {
public:
	string reason;
	string location_from;
	string location_to;
	int radius;
	int units;
	// TODO(schwehr): utc?  warning: day/month out of order
	int day_from;
	int month_from;
	int hour_from;
	int minute_from;

	int day_to;
	int month_to;
	int hour_to;
	int minute_to;
	int spare2;

	Ais8_1_13(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["reason"]=string2string(reason);
		res["location_from"]=string2string(location_from);
		res["location_to"]=string2string(location_to);
		res["radius"]=int2string(radius);
		res["units"]=int2string(units);
		res["day_from"]=int2string(day_from);
		res["month_from"]=int2string(month_from);
		res["hour_from"]=int2string(hour_from);
		res["minute_from"]=int2string(minute_from);
		res["day_to"]=int2string(day_to);
		res["month_to"]=int2string(month_to);
		res["hour_to"]=int2string(hour_to);
		res["minute_to"]=int2string(minute_to);
		res["spare2"]=int2string(spare2);


		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_13 &msg);

// IMO Circ 236 Extended ship static and voyage data
// Not to be transmitted after 2012-Jan-01
class Ais8_1_15 : public Ais8 {
public:
	float air_draught;
	int spare2;

	Ais8_1_15(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["air_draught"]=float2string(air_draught);
		res["spare2"]=int2string(spare2);
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_15 &msg);

// IMO Circ 236 Number of persons on board
class Ais8_1_16 : public Ais8 {
public:
	int persons;
	int spare2;

	Ais8_1_16(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["persons"]=int2string(persons);
		res["spare2"]=int2string(spare2);
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_16 &msg);

class Ais8_1_17_Target {
public:
	int type;
	string id;
	int spare;
	AisPoint position;
	int cog;
	int timestamp;
	int sog;

	Ais8_1_17_Target();
};

// IMO Circ 236 VTS Generated/synthetic targets
class Ais8_1_17 : public Ais8 {
public:
	vector<Ais8_1_17_Target> targets;

	Ais8_1_17(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		int c = 0;
		for (auto v:targets)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"targets%d_",c);
			std::string key = bufkey;
			++c;
			res[key+"type"]=int2string(v.type);
			res[key+"id"]=string2string(v.id);
			res[key+"spare"]=int2string(v.spare);
			AisPoint2string(v.position,key+"position",res);
			res[key+"cog"]=int2string(v.cog);
			res[key+"timestamp"]=int2string(v.timestamp);
			res[key+"sog"]=int2string(v.sog);
		}

		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_17 &msg);

// No 8_1_18

// IMO Circ 289 Marine traffic signal
class Ais8_1_19 : public Ais8 {
public:
	int link_id;
	string name;
	AisPoint position;  // funny bit count
	int status;
	int signal;
	int utc_hour_next;
	int utc_min_next;
	int next_signal;
	std::array<int, 4> spare2;

	Ais8_1_19(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["name"]=string2string(name);
		AisPoint2string(position,"position",res);
		res["status"]=int2string(status);
		res["signal"]=int2string(signal);
		res["utc_hour_next"]=int2string(utc_hour_next);
		res["utc_min_next"]=int2string(utc_min_next);
		res["next_signal"]=int2string(next_signal);
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_19 &msg);

// No message 8_1_20

// IMO Circ 289 Weather observation report from ship
class Ais8_1_21 : public Ais8 {
public:
	int type_wx_report;

	// TYPE 0
	string location;
	AisPoint position;  // 25, 24 bits
	int utc_day;
	int utc_hour;
	int utc_min;
	// wx - use wx[0]
	float horz_viz;  // nautical miles
	int humidity;  // %
	int wind_speed;  // ave knots
	int wind_dir;
	float pressure;  // hPa - float needed for type 1
	int pressure_tendency;
	float air_temp;  // C
	float water_temp;  // C
	int wave_period;  // s
	float wave_height;
	int wave_dir;
	float swell_height;  // m
	int swell_dir;
	int swell_period;  // s
	int spare2;

	// TYPE 1 - !@#$!!!!!
	// x, y
	int utc_month;
	// utc_day, hour, min
	int cog;
	float sog;
	int heading;  // Assume this is true degrees????
	// pressure defined in type 0
	float rel_pressure;  // 3 hour hPa
	// pressure_tendenc defined in type 0
	// wind_dir defined in type 0
	float wind_speed_ms;  // m/s
	int wind_dir_rel;
	float wind_speed_rel;  // m/s
	float wind_gust_speed;  // m/s
	int wind_gust_dir;
	int air_temp_raw;  // TODO(schwehr): Convert this to C.  Kelvin makes no sense
	// humidity defined in type 0
	// sea_temp_k
	int water_temp_raw;  // TODO(schwehr): fix this
	// hor_viz
	int wx[3];  // current, past 1, past 2
	int cloud_total;
	int cloud_low;
	int cloud_low_type;
	int cloud_middle_type;
	int cloud_high_type;
	float alt_lowest_cloud_base;
	// wave_period
	// wave_height
	// swell_dir
	// swell_period
	// swell_height
	int swell_dir_2;
	int swell_period_2;
	float swell_height_2;
	float ice_thickness;  // Network is cm, storing m.
	int ice_accretion;
	int ice_accretion_cause;
	int sea_ice_concentration;
	int amt_type_ice;
	int ice_situation;
	int ice_devel;
	int bearing_ice_edge;

	Ais8_1_21(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["type_wx_report"]=int2string(type_wx_report);
		res["location"]=string2string(location);
		AisPoint2string(position,"position",res);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["horz_viz"]=float2string(horz_viz);
		res["humidity"]=int2string(humidity);
		res["wind_speed"]=int2string(wind_speed);
		res["wind_dir"]=int2string(wind_dir);
		res["pressure"]=float2string(pressure);
		res["pressure_tendency"]=int2string(pressure_tendency);
		res["air_temp"]=float2string(air_temp);
		res["water_temp"]=float2string(water_temp);
		res["wave_period"]=int2string(wave_period);
		res["wave_height"]=float2string(wave_height);
		res["wave_dir"]=int2string(wave_dir);
		res["swell_height"]=float2string(swell_height);
		res["swell_dir"]=int2string(swell_dir);
		res["swell_period"]=int2string(swell_period);
		res["spare2"]=int2string(spare2);
		res["utc_month"]=int2string(utc_month);
		res["cog"]=int2string(cog);
		res["sog"]=float2string(sog);
		res["heading"]=int2string(heading);
		res["rel_pressure"]=float2string(rel_pressure);
		res["wind_speed_ms"]=float2string(wind_speed_ms);
		res["wind_dir_rel"]=int2string(wind_dir_rel);
		res["wind_speed_rel"]=float2string(wind_speed_rel);
		res["wind_gust_speed"]=float2string(wind_gust_speed);
		res["wind_gust_dir"]=int2string(wind_gust_dir);
		res["air_temp_raw"]=int2string(air_temp_raw);
		res["water_temp_raw"]=int2string(water_temp_raw);
		res["wx"]=intarraytostring(wx,3);
		res["cloud_total"]=int2string(cloud_total);
		res["cloud_low"]=int2string(cloud_low);
		res["cloud_low_type"]=int2string(cloud_low_type);
		res["cloud_middle_type"]=int2string(cloud_middle_type);
		res["cloud_high_type"]=int2string(cloud_high_type);
		res["alt_lowest_cloud_base"]=float2string(alt_lowest_cloud_base);
		res["swell_dir_2"]=int2string(swell_dir_2);
		res["swell_period_2"]=int2string(swell_period_2);
		res["swell_height_2"]=float2string(swell_height_2);
		res["ice_thickness"]=float2string(ice_thickness);
		res["ice_accretion"]=int2string(ice_accretion);
		res["ice_accretion_cause"]=int2string(ice_accretion_cause);
		res["sea_ice_concentration"]=int2string(sea_ice_concentration);
		res["amt_type_ice"]=int2string(amt_type_ice);
		res["ice_situation"]=int2string(ice_situation);
		res["ice_devel"]=int2string(ice_devel);
		res["bearing_ice_edge"]=int2string(bearing_ice_edge);

		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_21 &msg);


const size_t AIS8_1_22_NUM_NAMES = 128;
const size_t AIS8_1_22_SUBAREA_SIZE = 87;
extern const char *ais8_1_22_notice_names[AIS8_1_22_NUM_NAMES];

enum Ais8_1_22_AreaShapeEnum {
	AIS8_1_22_SHAPE_ERROR = -1,
	AIS8_1_22_SHAPE_CIRCLE = 0,  // OR Point.
	AIS8_1_22_SHAPE_RECT = 1,
	AIS8_1_22_SHAPE_SECTOR = 2,
	AIS8_1_22_SHAPE_POLYLINE = 3,
	AIS8_1_22_SHAPE_POLYGON = 4,
	AIS8_1_22_SHAPE_TEXT = 5,
	AIS8_1_22_SHAPE_RESERVED_6 = 6,
	AIS8_1_22_SHAPE_RESERVED_7 = 7
};

extern const char *ais8_1_22_shape_names[8];

// Sub-Areas for the Area Notice class

class Ais8_1_22_SubArea {
public:
	virtual Ais8_1_22_AreaShapeEnum getType() const = 0;
	virtual ~Ais8_1_22_SubArea() {}
	virtual std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"class"] = "SubArea";
		return res;
	}
};

unique_ptr<Ais8_1_22_SubArea>
ais8_1_22_subarea_factory(const AisBitset &bs,
						  const size_t offset);

// or Point if radius is 0
class Ais8_1_22_Circle : public Ais8_1_22_SubArea {
public:
	AisPoint position;  // Longitude and latitude.
	// Going to assume that the precision is not useful.
	int precision;  // How many decimal places for x and y.
	int radius_m;
	unsigned int spare;  // 18 bits.

	Ais8_1_22_Circle(const AisBitset &bs, const size_t offset);
	~Ais8_1_22_Circle() {}
	Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_CIRCLE;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"type"]="Circle";
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"radius_m"]=int2string(radius_m);
		return Ais8_1_22_SubArea::values(prefix,res);
	}
};

class Ais8_1_22_Rect : public Ais8_1_22_SubArea {
public:
	AisPoint position;  // Longitude and latitude.
	int precision;  // How many decimal places for x and y.  Useless.
	int e_dim_m;  // East dimension in meters.
	int n_dim_m;
	int orient_deg;  // Orientation in degrees from true north.
	unsigned int spare;  // 5 bits.

	Ais8_1_22_Rect(const AisBitset &bs, const size_t offset);
	~Ais8_1_22_Rect() {}
	Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_RECT;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"type"]="Rect";
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"e_dim_m"]=int2string(e_dim_m);
		res[prefix+"n_dim_m"]=int2string(n_dim_m);
		res[prefix+"orient_deg"]=int2string(orient_deg);
		res[prefix+"spare"]=int2string(spare);
		return Ais8_1_22_SubArea::values(prefix,res);
	}
};

class Ais8_1_22_Sector : public Ais8_1_22_SubArea {
public:
	AisPoint position;  // Longitude and latitude.
	// TODO(schwehr): precision in IMO, but not RTCM.  Double check.
	int precision;  // How many decimal places for x and y?
	int radius_m;
	int left_bound_deg;
	int right_bound_deg;

	Ais8_1_22_Sector(const AisBitset &bs, const size_t offset);
	~Ais8_1_22_Sector() {}
	Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_SECTOR;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Sector";
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"radius_m"]=int2string(radius_m);
		res[prefix+"left_bound_deg"]=int2string(left_bound_deg);
		res[prefix+"right_bound_deg"]=int2string(right_bound_deg);
		return Ais8_1_22_SubArea::values(prefix,res);
	}
};

// Or Waypoint
// Must have a point before on the VDL
// TODO(schwehr): do I bring in the prior point x, y, precision?
class Ais8_1_22_Polyline : public Ais8_1_22_SubArea {
public:
	// TODO(schwehr): int precision; // How many decimal places for x and y.

	// Up to 4 points
	vector<float> angles;
	vector<float> dists_m;
	unsigned int spare;  // 2 bit.

	Ais8_1_22_Polyline(const AisBitset &bs, const size_t offset);
	~Ais8_1_22_Polyline() {}
	Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_POLYLINE;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Polyline";

		res[prefix+"angles"]=vectorfloattostring(angles);
		res[prefix+"dists_m"]=vectorfloattostring(dists_m);
		return Ais8_1_22_SubArea::values(prefix,res);
	}
};

// TODO(schwehr): Bring in the prior point?  And do we fold the sub area data
// into one polygon if there are more than one?
class Ais8_1_22_Polygon : public Ais8_1_22_SubArea {
public:
	// TODO(schwehr): int precision; // How many decimal places for x and y.

	// Up to 4 points in a first message, but aggregated if multiple sub areas
	vector<float> angles;
	vector<float> dists_m;
	unsigned int spare;  // 2 bit

	Ais8_1_22_Polygon(const AisBitset &bs, const size_t offset);
	~Ais8_1_22_Polygon() {}
	Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_POLYGON;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Polygon";

		res[prefix+"angles"]=vectorfloattostring(angles);
		res[prefix+"dists_m"]=vectorfloattostring(dists_m);
		return Ais8_1_22_SubArea::values(prefix,res);
	}
};


class Ais8_1_22_Text : public Ais8_1_22_SubArea {
public:
	string text;
	// TODO(schwehr): spare?

	Ais8_1_22_Text(const AisBitset &bs, const size_t offset);
	~Ais8_1_22_Text() {}
	Ais8_1_22_AreaShapeEnum getType() const {return AIS8_1_22_SHAPE_TEXT;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Text";

		res[prefix+"text"]=string2string(text);

		return Ais8_1_22_SubArea::values(prefix,res);
	}
};

// Area Notice class

class Ais8_1_22 : public Ais8 {
public:
	int link_id;  // 10 bit id to match up text blocks.
	int notice_type;  // Area type / Notice Description.
	int month;  // These are in UTC.
	int day;
	int hour;
	int minute;
	int duration_minutes;  // Time from the start until the notice expires.

	// 1 or more sub messages
	vector<unique_ptr<Ais8_1_22_SubArea>> sub_areas;

	Ais8_1_22(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["notice_type"]=int2string(notice_type);
		res["month"]=int2string(month);
		res["day"]=int2string(day);
		res["hour"]=int2string(hour);
		res["minute"]=int2string(minute);
		res["duration_minutes"]=int2string(duration_minutes);

		int sz = sub_areas.size();
		for (int c=0;c<sz;++c)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"sub_areas%d_",c);
			std::string key = bufkey;
			res = sub_areas[c]->values(key,res);
		}
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream& o, Ais8_1_22 const& msg);

// No message 8_1_23

// IMO Circ 289 Extended ship static and voyage-related
class Ais8_1_24 : public Ais8 {
public:
	int link_id;
	float air_draught;  // m
	string last_port;
	std::array<std::string, 2> next_ports;

	// TODO(schwehr): enum list of param types
	std::array<int, 26> solas_status;  // 0 NA, 1 operational, 2 SNAFU, 3 no data
	int ice_class;
	int shaft_power;  // horses
	int vhf;
	string lloyds_ship_type;
	int gross_tonnage;
	int laden_ballast;
	int heavy_oil;
	int light_oil;
	int diesel;
	int bunker_oil;  // tonnes
	int persons;
	int spare2;

	Ais8_1_24(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["air_draught"]=float2string(air_draught);
		res["last_port"]=string2string(last_port);
		res["next_ports0"]=string2string(next_ports[0]);
		res["next_ports1"]=string2string(next_ports[1]);
		res["solas_status"]=arrayinttostring<26>(solas_status);
		res["ice_class"]=int2string(ice_class);
		res["shaft_power"]=int2string(shaft_power);
		res["vhf"]=int2string(vhf);
		res["lloyds_ship_type"]=string2string(lloyds_ship_type);
		res["gross_tonnage"]=int2string(gross_tonnage);
		res["laden_ballast"]=int2string(laden_ballast);
		res["heavy_oil"]=int2string(heavy_oil);
		res["light_oil"]=int2string(light_oil);
		res["diesel"]=int2string(diesel);
		res["bunker_oil"]=int2string(bunker_oil);
		res["persons"]=int2string(persons);
		res["spare2"]=int2string(spare2);


		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_24 &msg);

// No message 8_1_25

const size_t AIS8_1_26_REPORT_SIZE = 112;

enum Ais8_1_26_SensorEnum {
	AIS8_1_26_SENSOR_ERROR = -1,
	AIS8_1_26_SENSOR_LOCATION = 0,
	AIS8_1_26_SENSOR_STATION = 1,
	AIS8_1_26_SENSOR_WIND = 2,
	AIS8_1_26_SENSOR_WATER_LEVEL = 3,
	AIS8_1_26_SENSOR_CURR_2D = 4,
	AIS8_1_26_SENSOR_CURR_3D = 5,
	AIS8_1_26_SENSOR_HORZ_FLOW = 6,
	AIS8_1_26_SENSOR_SEA_STATE = 7,
	AIS8_1_26_SENSOR_SALINITY = 8,
	AIS8_1_26_SENSOR_WX = 9,
	AIS8_1_26_SENSOR_AIR_DRAUGHT = 10,
	AIS8_1_26_SENSOR_RESERVED_11 = 11,
	AIS8_1_26_SENSOR_RESERVED_12 = 12,
	AIS8_1_26_SENSOR_RESERVED_13 = 13,
	AIS8_1_26_SENSOR_RESERVED_14 = 14,
	AIS8_1_26_SENSOR_RESERVED_15 = 15,
};

class Ais8_1_26_SensorReport {
public:
	int report_type;
	int utc_day;
	int utc_hr;
	int utc_min;
	int site_id;  // aka link_id

	virtual Ais8_1_26_SensorEnum getType() const = 0;
	virtual ~Ais8_1_26_SensorReport() {}
	virtual std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const
	{
		std::map<std::string,std::string> res(std::move(super));

		res[prefix+"class"] = "SensorReport";
		res[prefix+"report_type"]=int2string(report_type);
		res[prefix+"utc_day"]=int2string(utc_day);
		res[prefix+"utc_hr"]=int2string(utc_hr);
		res[prefix+"utc_min"]=int2string(utc_min);
		res[prefix+"site_id"]=int2string(site_id);

		return res;
	}
};

Ais8_1_26_SensorReport*
ais8_1_26_sensor_report_factory(const AisBitset &bs,
								const size_t offset);

class Ais8_1_26_Location : public Ais8_1_26_SensorReport {
public:
	AisPoint position;
	float z;  // alt in m from MSL
	int owner;
	int timeout;
	int spare;

	Ais8_1_26_Location(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Location() {}
	Ais8_1_26_SensorEnum getType() const { return AIS8_1_26_SENSOR_LOCATION; }

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Location";

		AisPoint2string(position,prefix+"position",res);
		res[prefix+"z"]=float2string(z);
		res[prefix+"owner"]=int2string(owner);
		res[prefix+"timeout"]=int2string(timeout);
		res[prefix+"spare"]=int2string(spare);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_Station : public Ais8_1_26_SensorReport {
public:
	string name;
	int spare;

	Ais8_1_26_Station(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Station() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_STATION;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Station";

		res[prefix+"Station"]=string2string(name);
		res[prefix+"spare"]=int2string(spare);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_Wind : public Ais8_1_26_SensorReport {
public:
	int wind_speed;  // knots
	int wind_gust;  // knots
	int wind_dir;
	int wind_gust_dir;
	int sensor_type;
	int wind_forecast;  // knots
	int wind_gust_forecast;  // knots
	int wind_dir_forecast;
	int utc_day_forecast;
	int utc_hour_forecast;
	int utc_min_forecast;
	int duration;
	int spare;

	Ais8_1_26_Wind(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Wind() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_WIND;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Wind";

		res[prefix+"wind_speed"]=int2string(wind_speed);
		res[prefix+"wind_gust"]=int2string(wind_gust);
		res[prefix+"wind_dir"]=int2string(wind_dir);
		res[prefix+"wind_gust_dir"]=int2string(wind_gust_dir);
		res[prefix+"sensor_type"]=int2string(sensor_type);
		res[prefix+"wind_forecast"]=int2string(wind_forecast);
		res[prefix+"wind_gust_forecast"]=int2string(wind_gust_forecast);
		res[prefix+"wind_dir_forecast"]=int2string(wind_dir_forecast);
		res[prefix+"utc_day_forecast"]=int2string(utc_day_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"duration"]=int2string(duration);
		res[prefix+"spare"]=int2string(spare);


		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_WaterLevel : public Ais8_1_26_SensorReport {
public:
	int type;
	float level;  // m.  assuming it is being stored at 0.01 m inc.
	int trend;
	int vdatum;
	int sensor_type;
	int forecast_type;
	float level_forecast;
	int utc_day_forecast;
	int utc_hour_forecast;
	int utc_min_forecast;
	int duration;  // minutes
	int spare;

	Ais8_1_26_WaterLevel(const AisBitset &bs, const size_t offset);
	Ais8_1_26_WaterLevel() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_WATER_LEVEL;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="WaterLevel";

		res[prefix+"type"]=int2string(type);
		res[prefix+"level"]=float2string(level);
		res[prefix+"trend"]=int2string(trend);
		res[prefix+"vdatum"]=int2string(vdatum);
		res[prefix+"sensor_type"]=int2string(sensor_type);
		res[prefix+"forecast_type"]=int2string(forecast_type);
		res[prefix+"level_forecast"]=float2string(level_forecast);
		res[prefix+"utc_day_forecast"]=int2string(utc_day_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"duration"]=int2string(duration);
		res[prefix+"spare"]=int2string(spare);



		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_Curr2D_Current {
public:
	float speed;  // knots
	int dir;
	int depth;  // m
};

class Ais8_1_26_Curr2D : public Ais8_1_26_SensorReport {
public:
	Ais8_1_26_Curr2D_Current currents[3];
	int type;
	int spare;

	Ais8_1_26_Curr2D(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Curr2D() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_CURR_2D;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Curr2D";

		res[prefix+"type"]=int2string(type);
		res[prefix+"spare"]=int2string(spare);

		res[prefix+"speed0"]=float2string(currents[0].speed);
		res[prefix+"dir0"]=int2string(currents[0].dir);
		res[prefix+"depth0"]=int2string(currents[0].depth);
		res[prefix+"speed1"]=float2string(currents[1].speed);
		res[prefix+"dir1"]=int2string(currents[1].dir);
		res[prefix+"depth1"]=int2string(currents[1].depth);
		res[prefix+"speed2"]=float2string(currents[2].speed);
		res[prefix+"dir2"]=int2string(currents[2].dir);
		res[prefix+"depth2"]=int2string(currents[2].depth);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_Curr3D_Current {
public:
	float north;
	float east;
	float up;
	int depth;  // m
};

class Ais8_1_26_Curr3D : public Ais8_1_26_SensorReport {
public:
	Ais8_1_26_Curr3D_Current currents[2];
	int type;
	int spare;

	Ais8_1_26_Curr3D(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Curr3D() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_CURR_3D;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Curr3D";

		res[prefix+"type"]=int2string(type);
		res[prefix+"spare"]=int2string(spare);

		res[prefix+"north0"]=float2string(currents[0].north);
		res[prefix+"east0"]=float2string(currents[0].east);
		res[prefix+"up0"]=float2string(currents[0].up);
		res[prefix+"depth0"]=int2string(currents[0].depth);

		res[prefix+"north1"]=float2string(currents[1].north);
		res[prefix+"east1"]=float2string(currents[1].east);
		res[prefix+"up1"]=float2string(currents[1].up);
		res[prefix+"depth1"]=int2string(currents[1].depth);
		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_HorzFlow_Current {
public:
	int bearing;  // deg
	int dist;  // m
	float speed;  // knots
	int dir;  // deg
	int level;  // m
};

class Ais8_1_26_HorzFlow : public Ais8_1_26_SensorReport {
public:
	Ais8_1_26_HorzFlow_Current currents[2];
	int spare;

	Ais8_1_26_HorzFlow(const AisBitset &bs, const size_t offset);
	Ais8_1_26_HorzFlow() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_HORZ_FLOW;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="HorzFlow";

		res[prefix+"spare"]=int2string(spare);

		res[prefix+"bearing0"]=int2string(currents[0].bearing);
		res[prefix+"dist0"]=int2string(currents[0].dist);
		res[prefix+"speed0"]=float2string(currents[0].speed);
		res[prefix+"dir0"]=int2string(currents[0].dir);
		res[prefix+"level0"]=int2string(currents[0].level);

		res[prefix+"bearing1"]=int2string(currents[1].bearing);
		res[prefix+"dist1"]=int2string(currents[1].dist);
		res[prefix+"speed1"]=float2string(currents[1].speed);
		res[prefix+"dir1"]=int2string(currents[1].dir);
		res[prefix+"level1"]=int2string(currents[1].level);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_SeaState : public Ais8_1_26_SensorReport {
public:
	float swell_height;
	int swell_period;  // seconds
	int swell_dir;  // deg
	int sea_state;
	int swell_sensor_type;
	float water_temp;  // C
	float water_temp_depth;  // m
	int water_sensor_type;
	float wave_height;
	int wave_period;  // seconds
	int wave_dir;  // deg
	int wave_sensor_type;
	float salinity;

	Ais8_1_26_SeaState(const AisBitset &bs, const size_t offset);
	Ais8_1_26_SeaState() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_SEA_STATE;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="SeaState";

		res[prefix+"swell_height"]=float2string(swell_height);
		res[prefix+"swell_period"]=int2string(swell_period);
		res[prefix+"swell_dir"]=int2string(swell_dir);
		res[prefix+"sea_state"]=int2string(sea_state);
		res[prefix+"swell_sensor_type"]=int2string(swell_sensor_type);
		res[prefix+"water_temp"]=float2string(water_temp);
		res[prefix+"water_temp_depth"]=float2string(water_temp_depth);
		res[prefix+"water_sensor_type"]=int2string(water_sensor_type);
		res[prefix+"wave_height"]=float2string(wave_height);
		res[prefix+"wave_period"]=int2string(wave_period);
		res[prefix+"wave_dir"]=int2string(wave_dir);
		res[prefix+"wave_sensor_type"]=int2string(wave_sensor_type);
		res[prefix+"salinity"]=float2string(salinity);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_Salinity : public Ais8_1_26_SensorReport {
public:
	float water_temp;  // C
	float conductivity;  // siemens/m
	float pressure;  // decibars
	float salinity;  // 0/00 ppt
	int salinity_type;
	int sensor_type;
	int spare[2];

	Ais8_1_26_Salinity(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Salinity() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_SALINITY;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Salinity";
		res[prefix+"water_temp"]=float2string(water_temp);
		res[prefix+"conductivity"]=float2string(conductivity);
		res[prefix+"pressure"]=float2string(pressure);
		res[prefix+"salinity"]=float2string(salinity);
		res[prefix+"salinity_type"]=int2string(salinity_type);
		res[prefix+"sensor_type"]=int2string(sensor_type);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_Wx : public Ais8_1_26_SensorReport {
public:
	float air_temp;  // C
	int air_temp_sensor_type;
	int precip;
	float horz_vis;  // nm
	float dew_point;  // C
	int dew_point_type;
	float air_pressure;  // Pascals (Pa).
	int air_pressure_trend;
	int air_pressor_type;
	float salinity;  // 0/00 ppt
	int spare;

	Ais8_1_26_Wx(const AisBitset &bs, const size_t offset);
	Ais8_1_26_Wx() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_WX;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Wx";
		res[prefix+"air_temp"]=float2string(air_temp);
		res[prefix+"air_temp_sensor_type"]=int2string(air_temp_sensor_type);
		res[prefix+"precip"]=int2string(precip);
		res[prefix+"horz_vis"]=float2string(horz_vis);
		res[prefix+"dew_point"]=float2string(dew_point);
		res[prefix+"dew_point_type"]=int2string(dew_point_type);
		res[prefix+"air_pressure"]=float2string(air_pressure);
		res[prefix+"air_pressure_trend"]=int2string(air_pressure_trend);
		res[prefix+"air_pressor_type"]=int2string(air_pressor_type);
		res[prefix+"salinity"]=float2string(salinity);
		res[prefix+"spare"]=int2string(spare);

		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

class Ais8_1_26_AirDraught : public Ais8_1_26_SensorReport {
public:
	float draught;
	float gap;
	float forecast_gap;
	int trend;
	int utc_day_forecast;
	int utc_hour_forecast;
	int utc_min_forecast;
	int spare;

	Ais8_1_26_AirDraught(const AisBitset &bs, const size_t offset);
	Ais8_1_26_AirDraught() {}
	Ais8_1_26_SensorEnum getType() const {return AIS8_1_26_SENSOR_AIR_DRAUGHT;}

	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="AirDraught";
		res[prefix+"draught"]=float2string(draught);
		res[prefix+"gap"]=float2string(gap);
		res[prefix+"forecast_gap"]=float2string(forecast_gap);
		res[prefix+"trend"]=int2string(trend);
		res[prefix+"utc_day_forecast"]=int2string(utc_day_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"spare"]=int2string(spare);


		return Ais8_1_26_SensorReport::values(prefix,res);
	}
};

// IMO Circ 289 Environmental
class Ais8_1_26 : public Ais8 {
public:
	vector<Ais8_1_26_SensorReport *> reports;

	Ais8_1_26(const char *nmea_payload, const size_t pad);
	~Ais8_1_26();
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));

		int sz = reports.size();
		for (int c=0;c<sz;++c)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"reports%d_",c);
			std::string key = bufkey;
			res = reports[c]->values(key,res);
		}
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_26 &msg);


// IMO Circ 289 Route information
class Ais8_1_27 : public Ais8 {
public:
	int link_id;
	int sender_type;
	int route_type;
	int utc_month;
	int utc_day;
	int utc_hour;
	int utc_min;
	int duration;
	vector<AisPoint> waypoints;

	Ais8_1_27(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["sender_type"]=int2string(sender_type);
		res["route_type"]=int2string(route_type);
		res["utc_month"]=int2string(utc_month);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["duration"]=int2string(duration);

		int sz = waypoints.size();
		for (int c=0;c<sz;++c)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"waypoints%d_",c);
			std::string key = bufkey;
			AisPoint2string(waypoints[c],key,res);
		}
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_27 &msg);


//  No message 8_1_28


// IMO Circ 289 Text description
class Ais8_1_29 : public Ais8 {
public:
	int link_id;
	string text;
	int spare2;

	Ais8_1_29(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["text"]=string2string(text);
		res["spare2"]=int2string(spare2);

		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_29 &msg);


// No message 8_1_30

// IMO Circ 289 Meteorological and Hydrographic data
// Section 1, Table 1.1
// TODO(schwehr): is this exactly the same as 8_1_11 or has anything changed?
//       x,y swapped.
class Ais8_1_31 : public Ais8 {
public:
	AisPoint position;  // Opposite the bit order of 8_1_11
	int position_accuracy;  // New field
	int utc_day;
	int utc_hour;
	int utc_min;
	int wind_ave;  // kts
	int wind_gust;  // kts
	int wind_dir;
	int wind_gust_dir;
	float air_temp;  // C
	int rel_humid;
	float dew_point;
	float air_pres;  // Pascals (Pa).
	int air_pres_trend;
	float horz_vis;  // NM
	float water_level;  // m
	int water_level_trend;

	float surf_cur_speed;
	int surf_cur_dir;
	float cur_speed_2;  // kts
	int cur_dir_2;
	int cur_depth_2;  // m
	float cur_speed_3;  // kts
	int cur_dir_3;
	int cur_depth_3;  // m

	float wave_height;  // m
	int wave_period;
	int wave_dir;
	float swell_height;  // m
	int swell_period;
	int swell_dir;
	int sea_state;  // beaufort scale - Table 1.2
	float water_temp;
	int precip_type;
	float salinity;
	int ice;  // yes/no/undef/unknown
	int spare2;

	Ais8_1_31(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,"position",res);
		res["position_accuracy"]=int2string(position_accuracy);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["wind_ave"]=int2string(wind_ave);
		res["wind_gust"]=int2string(wind_gust);
		res["wind_dir"]=int2string(wind_dir);
		res["wind_gust_dir"]=int2string(wind_gust_dir);
		res["air_temp"]=float2string(air_temp);
		res["rel_humid"]=int2string(rel_humid);
		res["dew_point"]=float2string(dew_point);
		res["air_pres"]=float2string(air_pres);
		res["air_pres_trend"]=int2string(air_pres_trend);
		res["horz_vis"]=float2string(horz_vis);
		res["water_level"]=float2string(water_level);
		res["water_level_trend"]=int2string(water_level_trend);
		res["surf_cur_speed"]=float2string(surf_cur_speed);
		res["surf_cur_dir"]=int2string(surf_cur_dir);
		res["cur_speed_2"]=float2string(cur_speed_2);
		res["cur_dir_2"]=int2string(cur_dir_2);
		res["cur_depth_2"]=int2string(cur_depth_2);
		res["cur_speed_3"]=float2string(cur_speed_3);
		res["cur_dir_3"]=int2string(cur_dir_3);
		res["cur_depth_3"]=int2string(cur_depth_3);
		res["wave_height"]=float2string(wave_height);
		res["wave_period"]=int2string(wave_period);
		res["wave_dir"]=int2string(wave_dir);
		res["swell_height"]=float2string(swell_height);
		res["swell_period"]=int2string(swell_period);
		res["swell_dir"]=int2string(swell_dir);
		res["sea_state"]=int2string(sea_state);
		res["water_temp"]=float2string(water_temp);
		res["precip_type"]=int2string(precip_type);
		res["salinity"]=float2string(salinity);
		res["ice"]=int2string(ice);
		res["spare2"]=int2string(spare2);

		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_1_31 &msg);

// TODO(schwehr): Persons on board ITU 1371-1 8_1_40.

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Inland ship static and voyage related data
class Ais8_200_10 : public Ais8 {
public:
	string eu_id;  // European Vessel ID - 8 characters
	float length;  // m
	float beam;  // m
	int ship_type;
	int haz_cargo;
	float draught;
	int loaded;
	// Sensor quality.
	int speed_qual;
	int course_qual;
	int heading_qual;
	int spare2;

	Ais8_200_10(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["eu_id"]=string2string(eu_id);
		res["length"]=float2string(length);
		res["beam"]=float2string(beam);
		res["ship_type"]=int2string(ship_type);
		res["haz_cargo"]=int2string(haz_cargo);
		res["draught"]=float2string(draught);
		res["loaded"]=int2string(loaded);
		res["speed_qual"]=int2string(speed_qual);
		res["course_qual"]=int2string(course_qual);
		res["heading_qual"]=int2string(heading_qual);
		res["spare2"]=int2string(spare2);
		return Ais8::values(res);
	}
};

// http://www.ris.eu/docs/File/536/vessel_traking_and_tracing_standard_ed1-2_ccnr_23-apr_2013_en.pdf
// ETA at lock/bridge/terminal
class Ais8_200_21 : public Ais8 {
public:
	string country;         // UN country code         0 = not available = default
	string location;        // UN location code        0 = not available = default
	string section;         // Fairway section number  0 = not available = default
	string terminal;        // Terminal code           0 = not available = default
	string hectometre;      // Fairway hectometre      0 = not available = default
	// Examples for previous fields.  See:
	// http://www.ris.eu/docs/File/427/implementation_location_code_austria.pdf

	// ETA at lock/bridge/terminal - Estimated Time of Arrival; MMDDHHMM UTC
	int eta_month;          // 1 - 12;  0 = not available = default
	int eta_day;            // 1 - 31;  0 = not available = default
	int eta_hour;           // 0 - 23; 24 = not available = default
	int eta_minute;         // 0 - 59; 60 = not available = default
	int tugboats;           // 0 - 6,   7 = unknown = default
	// Maximum present static air draught 0
	float air_draught;      // 4000 (rest not used), in 1/100m, 0 = not used
	int spare2;             // 5 bits  Not used, should be set to zero.

	Ais8_200_21(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["country"]=string2string(country);
		res["location"]=string2string(location);
		res["section"]=string2string(section);
		res["terminal"]=string2string(terminal);
		res["hectometre"]=string2string(hectometre);
		res["eta_month"]=int2string(eta_month);
		res["eta_day"]=int2string(eta_day);
		res["eta_hour"]=int2string(eta_hour);
		res["eta_minute"]=int2string(eta_minute);
		res["tugboats"]=int2string(tugboats);
		res["air_draught"]=float2string(air_draught);
		res["spare2"]=int2string(spare2);

		return Ais8::values(res);
	}
};

// vessel_traking_and_tracing_standard_ed1-2_ccnr_23-apr_2013_en.pdf
// RTA at lock/bridge/terminal
class Ais8_200_22 : public Ais8 {
public:
	string country;         // UN country code         0 = not available
	string location;        // UN location code        0 = not available
	string section;         // Fairway section number  0 = not available
	string terminal;        // Terminal code           0 = not available
	string hectometre;      // Fairway hectometre      0 = not available
	// Examples for previous fields.  See:
	// http://www.ris.eu/docs/File/427/implementation_location_code_austria.pdf

	// RTA at lock/bridge/terminal - Recommended Time of Arrival; MMDDHHMM UTC
	int rta_month;          //  0 = not available
	int rta_day;            //  0 = not available
	int rta_hour;           // 24 = not available
	int rta_minute;         // 60 = not available
	int lock_status;        // Lock/bridge/terminal status
		// 0 = operational
		// 1 = limited operation
		// 2 = out of order
		// 3 = not available
	int spare2;             // Spare

	Ais8_200_22(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["country"]=string2string(country);
		res["location"]=string2string(location);
		res["section"]=string2string(section);
		res["terminal"]=string2string(terminal);
		res["hectometre"]=string2string(hectometre);
		res["rta_month"]=int2string(rta_month);
		res["rta_day"]=int2string(rta_day);
		res["rta_hour"]=int2string(rta_hour);
		res["rta_minute"]=int2string(rta_minute);
		res["lock_status"]=int2string(lock_status);
		res["spare2"]=int2string(spare2);
		return Ais8::values(res);
	}
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// EMMA warning
class Ais8_200_23 : public Ais8 {
public:
	int utc_year_start;
	int utc_month_start;
	int utc_day_start;
	int utc_year_end;
	int utc_month_end;
	int utc_day_end;
	int utc_hour_start;
	int utc_min_start;
	int utc_hour_end;
	int utc_min_end;
	AisPoint position1;
	AisPoint position2;
	int type;
	int min;
	int max;
	int classification;
	int wind_dir;  // EMMA CODE
	int spare2;

	Ais8_200_23(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["utc_year_start"]=int2string(utc_year_start);
		res["utc_month_start"]=int2string(utc_month_start);
		res["utc_day_start"]=int2string(utc_day_start);
		res["utc_year_end"]=int2string(utc_year_end);
		res["utc_month_end"]=int2string(utc_month_end);
		res["utc_day_end"]=int2string(utc_day_end);
		res["utc_hour_start"]=int2string(utc_hour_start);
		res["utc_min_start"]=int2string(utc_min_start);
		res["utc_hour_end"]=int2string(utc_hour_end);
		res["utc_min_end"]=int2string(utc_min_end);
		AisPoint2string(position1,"position1",res);
		AisPoint2string(position2,"position2",res);
		res["type"]=int2string(type);
		res["min"]=int2string(min);
		res["max"]=int2string(max);
		res["classification"]=int2string(classification);
		res["wind_dir"]=int2string(wind_dir);
		res["spare2"]=int2string(spare2);

		return Ais8::values(res);
	}
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Water Level
class Ais8_200_24 : public Ais8 {
public:
	// UN 2 letter code.  See Comtrade Country Code and Name.
	// https://unstats.un.org/unsd/tradekb/Knowledgebase/50377/Comtrade-Country-Code-and-Name
	string country;
	std::array<int, 4> gauge_ids;
	std::array<float, 4> levels;  // m

	Ais8_200_24(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["country"]=string2string(country);
		res["gauge_ids"]=arrayinttostring<4>(gauge_ids);
		res["levels"]=arrayfloattostring<4>(levels);
		return Ais8::values(res);
	}
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Signal status
class Ais8_200_40 : public Ais8 {
public:
	AisPoint position;
	int form;
	int dir;  // degrees
	int stream_dir;
	int status_raw;
	// TODO(schwehr): int status[9];  // WTF is the encoding for this?
	int spare2;

	Ais8_200_40(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,"position",res);
		res["form"]=int2string(form);
		res["dir"]=int2string(dir);
		res["stream_dir"]=int2string(stream_dir);
		res["status_raw"]=int2string(status_raw);
		res["spare2"]=int2string(spare2);

		return Ais8::values(res);
	}
};

// ECE-TRANS-SC3-2006-10e-RIS.pdf - River Information System
// Number of persons on board
class Ais8_200_55 : public Ais8 {
public:
	int crew;
	int passengers;
	int yet_more_personnel;
	std::array<int, 3> spare2;  // 51 spare bits.

	Ais8_200_55(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["crew"]=int2string(crew);
		res["passengers"]=int2string(passengers);
		res["yet_more_personnel"]=int2string(yet_more_personnel);
		res["spare2"]=arrayinttostring<3>(spare2);

		return Ais8::values(res);
	}
};

enum Ais8_366_22_AreaShapeEnum {
	AIS8_366_22_SHAPE_ERROR = -1,
	AIS8_366_22_SHAPE_CIRCLE = 0,
	AIS8_366_22_SHAPE_RECT = 1,
	AIS8_366_22_SHAPE_SECTOR = 2,
	AIS8_366_22_SHAPE_POLYLINE = 3,
	AIS8_366_22_SHAPE_POLYGON = 4,
	AIS8_366_22_SHAPE_TEXT = 5,
	AIS8_366_22_SHAPE_RESERVED_6 = 6,
	AIS8_366_22_SHAPE_RESERVED_7 = 7
};

extern const char *shape_names[8];

class Ais8_366_22_SubArea {
public:
	virtual Ais8_366_22_AreaShapeEnum getType() = 0;
	virtual ~Ais8_366_22_SubArea() { }
	virtual std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"class"] = "SubArea";
		return res;
	}
};

unique_ptr<Ais8_366_22_SubArea>
ais8_366_22_subarea_factory(const AisBitset &bs,
							const size_t offset);

// or Point if radius is 0
class Ais8_366_22_Circle : public Ais8_366_22_SubArea {
public:
	AisPoint position;
	// TODO(schwehr): int precision
	int radius_m;
	unsigned int spare;

	Ais8_366_22_Circle(const AisBitset &bs, const size_t offset);
	~Ais8_366_22_Circle() {}
	Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_CIRCLE;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Circle";
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"type"]="Circle";
		res[prefix+"radius_m"]=int2string(radius_m);
		return Ais8_366_22_SubArea::values(prefix,res);
	}
};

class Ais8_366_22_Rect : public Ais8_366_22_SubArea {
public:
	AisPoint position;  // longitude and latitude
	// TODO(schwehr): int precision
	int e_dim_m;  // East dimension in meters
	int n_dim_m;
	int orient_deg;  // Orientation in degrees from true north
	unsigned int spare;  // 5 bits

	Ais8_366_22_Rect(const AisBitset &bs, const size_t offset);
	~Ais8_366_22_Rect() {}
	Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_RECT;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"type"]="Rect";
		res[prefix+"e_dim_m"]=int2string(e_dim_m);
		res[prefix+"n_dim_m"]=int2string(n_dim_m);
		res[prefix+"orient_deg"]=int2string(orient_deg);
		res[prefix+"spare"]=int2string(spare);
		return Ais8_366_22_SubArea::values(prefix,res);
	}
};

class Ais8_366_22_Sector : public Ais8_366_22_SubArea {
public:
	AisPoint position;
	// TODO(schwehr): int precision
	int radius_m;
	int left_bound_deg;
	int right_bound_deg;
	// TODO(schwehr): spare?

	Ais8_366_22_Sector(const AisBitset &bs, const size_t offset);
	~Ais8_366_22_Sector() {}
	Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_SECTOR;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Sector";
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"radius_m"]=int2string(radius_m);
		res[prefix+"left_bound_deg"]=int2string(left_bound_deg);
		res[prefix+"right_bound_deg"]=int2string(right_bound_deg);
		return Ais8_366_22_SubArea::values(prefix,res);
	}
};

// Or Waypoint
// Must have a point before on the VDL, but pulled together here.
class Ais8_366_22_Polyline : public Ais8_366_22_SubArea {
public:
	AisPoint position;  // longitude and latitude
	// TODO(schwehr): precision

	// Up to 4 points
	vector<float> angles;
	vector<float> dists_m;
	unsigned int spare;

	Ais8_366_22_Polyline(const AisBitset &bs, const size_t offset);
	~Ais8_366_22_Polyline() {}
	Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_POLYLINE;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Polyline";
		res[prefix+"angles"]=vectorfloattostring(angles);
		res[prefix+"dists_m"]=vectorfloattostring(dists_m);
		return Ais8_366_22_SubArea::values(prefix,res);
	}
};

class Ais8_366_22_Polygon : public Ais8_366_22_SubArea {
public:
	AisPoint position;  // longitude and latitude
	// TODO(schwehr): precision?

	// Up to 4 points in a first message, but aggregated if multiple sub areas
	vector<float> angles;
	vector<float> dists_m;
	unsigned int spare;

	Ais8_366_22_Polygon(const AisBitset &bs, const size_t offset);
	~Ais8_366_22_Polygon() {}
	Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_POLYGON;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Polygon";
		res[prefix+"angles"]=vectorfloattostring(angles);
		res[prefix+"dists_m"]=vectorfloattostring(dists_m);
		return Ais8_366_22_SubArea::values(prefix,res);
	}
};

class Ais8_366_22_Text : public Ais8_366_22_SubArea {
public:
	string text;
	unsigned int spare;  // 3 bits

	Ais8_366_22_Text(const AisBitset &bs, const size_t offset);
	~Ais8_366_22_Text() {}
	Ais8_366_22_AreaShapeEnum getType() {return AIS8_366_22_SHAPE_TEXT;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Text";

		res[prefix+"text"]=string2string(text);
		return Ais8_366_22_SubArea::values(prefix,res);
	}
};

class Ais8_366_22 : public Ais8 {
public:
	// Common block at the front
	int link_id;  // 10 bit id to match up text blocks
	int notice_type;  // area_type / Notice Description
	int month;  // These really are in utc
	int day;
	int utc_hour;
	int utc_minute;
	int duration_minutes;  // Time from the start until the notice expires
	// 1 or more sub messages

	vector<unique_ptr<Ais8_366_22_SubArea>> sub_areas;

	Ais8_366_22(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["link_id"]=int2string(link_id);
		res["notice_type"]=int2string(notice_type);
		res["month"]=int2string(month);
		res["day"]=int2string(day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_minute"]=int2string(utc_minute);
		res["duration_minutes"]=int2string(duration_minutes);

		int sz = sub_areas.size();
		for (int c=0;c<sz;++c)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"sub_areas%d_",c);
			std::string key = bufkey;
			res = sub_areas[c]->values(key,res);
		}
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream& o, Ais8_366_22 const& msg);

const size_t AIS8_366_22_NUM_NAMES = 128;
extern const char *ais8_366_22_notice_names[AIS8_366_22_NUM_NAMES];

// 366 34 - Kurt older whale message 2008-2010
// TODO(schwehr): Ais8_366_34

// USCG Blue Force encrypted message.
class Ais8_366_56 : public Ais8 {
public:
	vector<unsigned char> encrypted;

	Ais8_366_56(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		std::string ele;

		for (unsigned char c:encrypted)
		{
			char buf[64];
			snprintf(buf,64,"%02X",(unsigned int)c);
			ele += buf;
		}
		res["encrypted"] = ele;

		return Ais8::values(res);
	}
};

class Ais8_367_22_SubArea {
public:
	virtual Ais8_366_22_AreaShapeEnum getType() const = 0;
	virtual ~Ais8_367_22_SubArea() { }
	virtual std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"class"] = "SubArea";
		return res;
	}

};

unique_ptr<Ais8_367_22_SubArea>
ais8_367_22_subarea_factory(const AisBitset &bs,
							const size_t offset);

class Ais8_367_22_Circle : public Ais8_367_22_SubArea {
public:
	AisPoint position;
	int precision;
	int radius_m;
	unsigned int spare;

	Ais8_367_22_Circle(const AisBitset &bs, const size_t offset);
	~Ais8_367_22_Circle() {}
	Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_CIRCLE;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"type"]="Circle";
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"radius_m"]=int2string(radius_m);
		return Ais8_367_22_SubArea::values(prefix,res);
	}
};

class Ais8_367_22_Rect : public Ais8_367_22_SubArea {
public:
	AisPoint position;
	int precision;
	int e_dim_m;
	int n_dim_m;
	int orient_deg;
	unsigned int spare;

	Ais8_367_22_Rect(const AisBitset &bs, const size_t offset);
	~Ais8_367_22_Rect() {}
	Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_RECT;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"type"]="Rect";
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"e_dim_m"]=int2string(e_dim_m);
		res[prefix+"n_dim_m"]=int2string(n_dim_m);
		res[prefix+"orient_deg"]=int2string(orient_deg);
		res[prefix+"spare"]=int2string(spare);
		return Ais8_367_22_SubArea::values(prefix,res);
	}
};

class Ais8_367_22_Sector : public Ais8_367_22_SubArea {
public:
	AisPoint position;
	int precision;
	int radius_m;
	int left_bound_deg;
	int right_bound_deg;
	int spare;

	Ais8_367_22_Sector(const AisBitset &bs, const size_t offset);
	~Ais8_367_22_Sector() {}
	Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_SECTOR;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Sector";
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"radius_m"]=int2string(radius_m);
		res[prefix+"left_bound_deg"]=int2string(left_bound_deg);
		res[prefix+"right_bound_deg"]=int2string(right_bound_deg);
		return Ais8_367_22_SubArea::values(prefix,res);
	}
};

// Polyline or Polygon
class Ais8_367_22_Poly : public Ais8_367_22_SubArea {
public:
	Ais8_366_22_AreaShapeEnum shape;
	AisPoint position;
	int precision;

	// Up to 4 points
	vector<float> angles;
	vector<float> dists_m;
	unsigned int spare;

	Ais8_367_22_Poly(const AisBitset &bs, const size_t offset,
					 Ais8_366_22_AreaShapeEnum area_shape);
	~Ais8_367_22_Poly() {}
	Ais8_366_22_AreaShapeEnum getType() const {return shape;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Poly";

		res[prefix+"angles"]=vectorfloattostring(angles);
		res[prefix+"dists_m"]=vectorfloattostring(dists_m);
		return Ais8_367_22_SubArea::values(prefix,res);
	}
};

class Ais8_367_22_Text : public Ais8_367_22_SubArea {
public:
	string text;
	unsigned int spare;  // 3 bits

	Ais8_367_22_Text(const AisBitset &bs, const size_t offset);
	~Ais8_367_22_Text() {}
	Ais8_366_22_AreaShapeEnum getType() const {return AIS8_366_22_SHAPE_TEXT;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Text";

		res[prefix+"text"]=string2string(text);

		return Ais8_367_22_SubArea::values(prefix,res);
	}
};

class Ais8_367_22 : public Ais8 {
public:
	int version;
	int link_id;
	int notice_type;
	int month;
	int day;
	int hour;
	int minute;
	int duration_minutes;
	int spare2;

	vector<unique_ptr<Ais8_367_22_SubArea>> sub_areas;

	Ais8_367_22(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["version"]=int2string(version);
		res["link_id"]=int2string(link_id);
		res["notice_type"]=int2string(notice_type);
		res["month"]=int2string(month);
		res["day"]=int2string(day);
		res["hour"]=int2string(hour);
		res["minute"]=int2string(minute);
		res["duration_minutes"]=int2string(duration_minutes);

		int sz = sub_areas.size();
		for (int c=0;c<sz;++c)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"sub_areas%d_",c);
			std::string key = bufkey;
			res = sub_areas[c]->values(key,res);
		}
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream& o, Ais8_367_22 const& msg);

// SSW Satellite Ship Weather 1-Slot Version
// https://www.e-navigation.nl/content/satellite-ship-weather
class Ais8_367_23 : public Ais8 {
public:
	int version = 0;

	int utc_day = 0;
	int utc_hour = 0;
	int utc_min = 0;

	AisPoint position = {0.0, 0.0};  // 25, 24 bits
	int pressure = 0;  // hPa
	int air_temp_raw = 0;  // raw reading, used for detecting N/A values
	float air_temp = 0.0;  // C
	int wind_speed = 0;  // knots
	int wind_gust = 0;  // knots
	int wind_dir = 0;  // degrees

	int spare2 = 0;

	Ais8_367_23(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["version"]=int2string(version);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		AisPoint2string(position,"position",res);
		res["pressure"]=int2string(pressure);
		res["air_temp_raw"]=int2string(air_temp_raw);
		res["air_temp"]=float2string(air_temp);
		res["wind_speed"]=int2string(wind_speed);
		res["wind_gust"]=int2string(wind_gust);
		res["wind_dir"]=int2string(wind_dir);

		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_367_23 &msg);

// SSW Satellite Ship Weather 1-Slot Version
// https://www.e-navigation.nl/content/satellite-ship-weather-small
class Ais8_367_24 : public Ais8 {
public:
	unsigned int version = 0;

	unsigned int utc_hour = 0;
	unsigned int utc_min = 0;

	AisPoint position = {0.0, 0.0};  // 25, 24 bits
	unsigned int pressure = 0;  // hPa

	Ais8_367_24(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["version"]=unsignedint2string(version);
		res["utc_hour"]=unsignedint2string(utc_hour);
		res["utc_min"]=unsignedint2string(utc_min);
		AisPoint2string(position,"position",res);
		res["pressure"]=unsignedint2string(pressure);


		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_367_24 &msg);

//
// SSW Satellite Ship Weather Tiny Version
// https://www.e-navigation.nl/content/satellite-ship-weather-tiny
class Ais8_367_25 : public Ais8 {
public:
	int version = 0;
	int utc_day = 0;
	int utc_hour = 0;
	int utc_min = 0;

	int pressure = 0;  // hPa
	int wind_speed = 0;  // Knots
	int wind_dir = 0;  // Degrees

	Ais8_367_25(const char *nmea_payload, const size_t pad);

	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["version"]=int2string(version);
		res["utc_day"]=int2string(utc_day);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["pressure"]=int2string(pressure);
		res["wind_speed"]=int2string(wind_speed);
		res["wind_dir"]=int2string(wind_dir);

		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_367_25 &msg);

// Environmental Message
// https://www.e-navigation.nl/content/environmental-message-0
// https://www.e-navigation.nl/sites/default/files/asm_files/em_version_release_3-23mar15_0.pdf
const size_t AIS8_367_33_REPORT_SIZE = 112;

enum Ais8_367_33_SensorEnum {
	AIS8_367_33_SENSOR_ERROR = -1,
	AIS8_367_33_SENSOR_LOCATION = 0,
	AIS8_367_33_SENSOR_STATION = 1,
	AIS8_367_33_SENSOR_WIND = 2,
	AIS8_367_33_SENSOR_WATER_LEVEL = 3,
	AIS8_367_33_SENSOR_CURR_2D = 4,
	AIS8_367_33_SENSOR_CURR_3D = 5,
	AIS8_367_33_SENSOR_HORZ_FLOW = 6,
	AIS8_367_33_SENSOR_SEA_STATE = 7,
	AIS8_367_33_SENSOR_SALINITY = 8,
	AIS8_367_33_SENSOR_WX = 9,
	AIS8_367_33_SENSOR_AIR_GAP = 10,
	AIS8_367_33_SENSOR_WIND_REPORT_2 = 11,
	AIS8_367_33_SENSOR_RESERVED_12 = 12,
	AIS8_367_33_SENSOR_RESERVED_13 = 13,
	AIS8_367_33_SENSOR_RESERVED_14 = 14,
	AIS8_367_33_SENSOR_RESERVED_15 = 15,
};

class Ais8_367_33_SensorReport {
public:
	Ais8_367_33_SensorEnum report_type = AIS8_367_33_SENSOR_ERROR;
	int utc_day = 0;
	int utc_hr = 0;
	int utc_min = 0;
	int site_id = 0;

	virtual Ais8_367_33_SensorEnum getType() const = 0;
	virtual ~Ais8_367_33_SensorReport() {}
	virtual std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const
	{
		std::map<std::string,std::string> res(std::move(super));

		res[prefix+"class"] = "SensorReport";
		res[prefix+"report_type"]=int2string(report_type);
		res[prefix+"utc_day"]=int2string(utc_day);
		res[prefix+"utc_hr"]=int2string(utc_hr);
		res[prefix+"utc_min"]=int2string(utc_min);
		res[prefix+"site_id"]=int2string(site_id);
		return res;
	}
};

unique_ptr<Ais8_367_33_SensorReport>
ais8_367_33_sensor_report_factory(const AisBitset &bs,
								  const size_t offset);

class Ais8_367_33_Location : public Ais8_367_33_SensorReport {
public:
	int version = 0;
	AisPoint position = {0.0, 0.0};
	int precision = 0;
	int altitude_raw = 0;  // 0.1 Meter
	float altitude = 0.0;  // Meters
	int owner = 0;
	int timeout = 0;
	int spare2 = 0;

	Ais8_367_33_Location(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Location() {}
	Ais8_367_33_SensorEnum getType() const override { return AIS8_367_33_SENSOR_LOCATION; }
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Location";
		res[prefix+"version"]=int2string(version);
		AisPoint2string(position,prefix+"position",res);
		res[prefix+"precision"]=int2string(precision);
		res[prefix+"altitude_raw"]=int2string(altitude_raw);
		res[prefix+"altitude"]=float2string(altitude);
		res[prefix+"owner"]=int2string(owner);
		res[prefix+"timeout"]=int2string(timeout);
		res[prefix+"spare2"]=int2string(spare2);
		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Station : public Ais8_367_33_SensorReport {
public:
	std::string name;
	int spare2 = 0;

	Ais8_367_33_Station(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Station() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_STATION;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Station";

		res[prefix+"Station"]=string2string(name);
		res[prefix+"spare2"]=int2string(spare2);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Wind : public Ais8_367_33_SensorReport {
public:
	int wind_speed = 0;  // Knots
	int wind_gust = 0;  // Knots
	int wind_dir = 0;  // Degrees
	int wind_gust_dir = 0;  // Degrees
	int sensor_type = 0;
	int wind_forecast = 0;  // Knots
	int wind_gust_forecast = 0;  // Knots
	int wind_dir_forecast = 0;  // Degrees
	int utc_day_forecast = 0;
	int utc_hour_forecast = 0;
	int utc_min_forecast = 0;
	int duration = 0;  // Minutes
	int spare2 = 0;

	Ais8_367_33_Wind(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Wind() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_WIND;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Wind";

		res[prefix+"wind_speed"]=int2string(wind_speed);
		res[prefix+"wind_gust"]=int2string(wind_gust);
		res[prefix+"wind_dir"]=int2string(wind_dir);
		res[prefix+"wind_gust_dir"]=int2string(wind_gust_dir);
		res[prefix+"sensor_type"]=int2string(sensor_type);
		res[prefix+"wind_forecast"]=int2string(wind_forecast);
		res[prefix+"wind_gust_forecast"]=int2string(wind_gust_forecast);
		res[prefix+"wind_dir_forecast"]=int2string(wind_dir_forecast);
		res[prefix+"utc_day_forecast"]=int2string(utc_day_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"duration"]=int2string(duration);
		res[prefix+"spare2"]=int2string(spare2);


		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_WaterLevel : public Ais8_367_33_SensorReport {
public:
	int type = 0;
	int level = 0;  // Centimeters
	int trend = 0;
	int vdatum = 0;
	int sensor_type = 0;
	int forecast_type = 0;
	int level_forecast = 0;  // Centimeters
	int utc_day_forecast = 0;
	int utc_hour_forecast = 0;
	int utc_min_forecast = 0;
	int duration = 0;  // Minutes
	int spare2 = 0;

	Ais8_367_33_WaterLevel(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_WaterLevel() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_WATER_LEVEL;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="WaterLevel";

		res[prefix+"type"]=int2string(type);
		res[prefix+"level"]=float2string(level);
		res[prefix+"trend"]=int2string(trend);
		res[prefix+"vdatum"]=int2string(vdatum);
		res[prefix+"sensor_type"]=int2string(sensor_type);
		res[prefix+"forecast_type"]=int2string(forecast_type);
		res[prefix+"level_forecast"]=float2string(level_forecast);
		res[prefix+"utc_day_forecast"]=int2string(utc_day_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"duration"]=int2string(duration);
		res[prefix+"spare2"]=int2string(spare2);



		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Curr2D_Current {
public:
	int speed_raw = 0;  // 0.1 Knots
	float speed = 0.0;  // Knots
	int dir = 0;  // Degrees
	int depth = 0;  // Meters
};

class Ais8_367_33_Curr2D : public Ais8_367_33_SensorReport {
public:
	std::array<Ais8_367_33_Curr2D_Current, 3> currents;
	int type = 0;
	int spare2 = 0;

	Ais8_367_33_Curr2D(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Curr2D() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_CURR_2D;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Curr2D";

		res[prefix+"type"]=int2string(type);
		res[prefix+"spare"]=int2string(spare2);

		res[prefix+"speed0"]=float2string(currents[0].speed);
		res[prefix+"dir0"]=int2string(currents[0].dir);
		res[prefix+"depth0"]=int2string(currents[0].depth);
		res[prefix+"speed1"]=float2string(currents[1].speed);
		res[prefix+"dir1"]=int2string(currents[1].dir);
		res[prefix+"depth1"]=int2string(currents[1].depth);
		res[prefix+"speed2"]=float2string(currents[2].speed);
		res[prefix+"dir2"]=int2string(currents[2].dir);
		res[prefix+"depth2"]=int2string(currents[2].depth);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Curr3D_Current {
public:
	int north_raw = 0;  // 0.1 knots
	float north = 0.0;  // Knots

	int east_raw = 0;  // 0.1 knots
	float east = 0.0;  // Knots

	int up_raw = 0;  // 0.1 knots
	float up = 0.0;  // Knots

	int depth = 0;  // Meters
};

class Ais8_367_33_Curr3D : public Ais8_367_33_SensorReport {
public:
	std::array<Ais8_367_33_Curr3D_Current, 2> currents;
	int type = 0;
	int spare2 = 0;

	Ais8_367_33_Curr3D(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Curr3D() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_CURR_3D;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Curr3D";

		res[prefix+"type"]=int2string(type);
		res[prefix+"north0"]=float2string(currents[0].north);
		res[prefix+"east0"]=float2string(currents[0].east);
		res[prefix+"up0"]=float2string(currents[0].up);
		res[prefix+"north_raw0"]=float2string(currents[0].north_raw);
		res[prefix+"east_raw0"]=float2string(currents[0].east_raw);
		res[prefix+"up_raw0"]=float2string(currents[0].up_raw);
		res[prefix+"depth0"]=int2string(currents[0].depth);

		res[prefix+"north1"]=float2string(currents[1].north);
		res[prefix+"east1"]=float2string(currents[1].east);
		res[prefix+"up1"]=float2string(currents[1].up);
		res[prefix+"north_raw1"]=float2string(currents[1].north_raw);
		res[prefix+"east_raw1"]=float2string(currents[1].east_raw);
		res[prefix+"up_raw1"]=float2string(currents[1].up_raw);
		res[prefix+"depth1"]=int2string(currents[1].depth);
		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_HorzFlow_Current {
public:
	int dist = 0;  // Meters
	int speed_raw = 0;  // 0.1 Knots
	float speed = 0.0;  // Knots
	int dir = 0;  // Degrees
	int level = 0;  // Meters
};

class Ais8_367_33_HorzFlow : public Ais8_367_33_SensorReport {
public:
	std::array<Ais8_367_33_HorzFlow_Current, 2> currents;
	int bearing = 0;  // Degrees
	int type = 0;
	int spare2 = 0;

	Ais8_367_33_HorzFlow(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_HorzFlow() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_HORZ_FLOW;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="HorzFlow";

		res[prefix+"dist0"]=int2string(currents[0].dist);
		res[prefix+"speed0"]=float2string(currents[0].speed);
		res[prefix+"speed_raw0"]=int2string(currents[0].speed_raw);
		res[prefix+"dir0"]=int2string(currents[0].dir);
		res[prefix+"level0"]=int2string(currents[0].level);

		res[prefix+"dist1"]=int2string(currents[1].dist);
		res[prefix+"speed1"]=float2string(currents[1].speed);
		res[prefix+"speed_raw1"]=int2string(currents[1].speed_raw);
		res[prefix+"dir1"]=int2string(currents[1].dir);
		res[prefix+"level1"]=int2string(currents[1].level);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_SeaState : public Ais8_367_33_SensorReport {
public:
	int swell_height_raw = 0;  // 0.1 Meter increments
	float swell_height = 0.0;  // Meters

	int swell_period = 0;  // Seconds
	int swell_dir = 0;  // Degrees
	int sea_state = 0;
	int swell_sensor_type = 0;

	int water_temp_raw = 0;  // 0.1 degrees C
	float water_temp = 0.0;  // Celsius

	int water_temp_depth_raw = 0;  // 0.1 Meters
	float water_temp_depth = 0.0;  // Meters

	int water_sensor_type = 0;

	int wave_height_raw = 0;  // 0.1 Meters
	float wave_height = 0.0;  // Meters

	int wave_period = 0;  // Seconds
	int wave_dir = 0;  // Degrees
	int wave_sensor_type = 0;

	int salinity_raw = 0;  // 0.1%
	float salinity = 0.0;  // %

	Ais8_367_33_SeaState(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_SeaState() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_SEA_STATE;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="SeaState";

		res[prefix+"swell_height"]=float2string(swell_height);
		res[prefix+"swell_height_raw"]=int2string(swell_height_raw);
		res[prefix+"swell_period"]=int2string(swell_period);
		res[prefix+"swell_dir"]=int2string(swell_dir);
		res[prefix+"sea_state"]=int2string(sea_state);
		res[prefix+"swell_sensor_type"]=int2string(swell_sensor_type);
		res[prefix+"water_temp"]=float2string(water_temp);
		res[prefix+"water_temp_depth"]=float2string(water_temp_depth);
		res[prefix+"water_temp_depth_raw"]=int2string(water_temp_depth_raw);
		res[prefix+"water_sensor_type"]=int2string(water_sensor_type);
		res[prefix+"wave_height"]=float2string(wave_height);
		res[prefix+"wave_height_raw"]=int2string(wave_height_raw);
		res[prefix+"wave_period"]=int2string(wave_period);
		res[prefix+"wave_dir"]=int2string(wave_dir);
		res[prefix+"wave_sensor_type"]=int2string(wave_sensor_type);
		res[prefix+"salinity"]=float2string(salinity);
		res[prefix+"salinity_raw"]=int2string(salinity_raw);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Salinity : public Ais8_367_33_SensorReport {
public:
	int water_temp_raw = 0;  // 0.1 degrees Celsius
	float water_temp = 0.0;  // Celsius

	int conductivity_raw = 0;  // 0.01 Siemens/Meter
	float conductivity = 0.0;  // Siemens/Meter

	int pressure_raw = 0;  // 0.1 decibars
	float pressure = 0.0;  // decibars

	int salinity_raw = 0;  // 0.1%
	float salinity = 0.0;  // 0/00 ppt

	int salinity_type = 0;
	int sensor_type = 0;
	int spare2[2] = {0, 0};

	Ais8_367_33_Salinity(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Salinity() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_SALINITY;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Salinity";
		res[prefix+"water_temp"]=float2string(water_temp);
		res[prefix+"conductivity"]=float2string(conductivity);
		res[prefix+"pressure"]=float2string(pressure);
		res[prefix+"salinity"]=float2string(salinity);
		res[prefix+"salinity_type"]=int2string(salinity_type);
		res[prefix+"sensor_type"]=int2string(sensor_type);
		res[prefix+"water_temp_raw"]=int2string(water_temp_raw);
		res[prefix+"conductivity_raw"]=int2string(conductivity_raw);
		res[prefix+"pressure_raw"]=int2string(pressure_raw);
		res[prefix+"salinity_raw"]=int2string(salinity_raw);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Wx : public Ais8_367_33_SensorReport {
public:
	int air_temp_raw = 0;  // 0.1 degrees Celsius
	float air_temp = 0.0;  // Celsius

	int air_temp_sensor_type = 0;
	int precip = 0;

	int horz_vis_raw = 0;  // 0.1 Nautical Miles
	float horz_vis = 0.0;  // Nautical Miles

	int dew_point_raw = 0;  // 0.1 degrees Celsius
	float dew_point = 0.0;  // Celsius

	int dew_point_type = 0;

	int air_pressure_raw = 0;  // 1 hPA
	int air_pressure = 0;  // Pascals (Pa).

	int air_pressure_trend = 0;
	int air_pressure_sensor_type = 0;

	int salinity_raw = 0;  // 0.1%
	float salinity = 0.0;  // %

	int spare2 = 0;

	Ais8_367_33_Wx(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Wx() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_WX;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Wx";
		res[prefix+"air_temp"]=float2string(air_temp);
		res[prefix+"air_temp_sensor_type"]=int2string(air_temp_sensor_type);
		res[prefix+"precip"]=int2string(precip);
		res[prefix+"horz_vis"]=float2string(horz_vis);
		res[prefix+"dew_point"]=float2string(dew_point);
		res[prefix+"dew_point_type"]=int2string(dew_point_type);
		res[prefix+"air_pressure"]=float2string(air_pressure);
		res[prefix+"air_pressure_trend"]=int2string(air_pressure_trend);
		res[prefix+"air_pressure_sensor_type"]=int2string(air_pressure_sensor_type);
		res[prefix+"salinity"]=float2string(salinity);
		res[prefix+"spare2"]=int2string(spare2);
		res[prefix+"air_temp_raw"]=int2string(air_temp_raw);
		res[prefix+"horz_vis_raw"]=int2string(horz_vis_raw);
		res[prefix+"dew_point_raw"]=int2string(dew_point_raw);
		res[prefix+"air_pressure_raw"]=int2string(air_pressure_raw);
		res[prefix+"salinity_raw"]=int2string(salinity_raw);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_AirGap : public Ais8_367_33_SensorReport {
public:
	int air_draught = 0;  // Centimeters
	int air_gap = 0;  // Centimeters
	int air_gap_trend = 0;
	int predicted_air_gap = 0;  // Centimeters

	int utc_day_forecast = 0;
	int utc_hour_forecast = 0;
	int utc_min_forecast = 0;

	int type = 0;
	int spare2 = 0;

	Ais8_367_33_AirGap(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_AirGap() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_AIR_GAP;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="AirGap";
		res[prefix+"air_draught"]=int2string(air_draught);
		res[prefix+"air_gap"]=int2string(air_gap);
		res[prefix+"air_gap_trend"]=int2string(air_gap_trend);
		res[prefix+"predicted_air_gap"]=int2string(predicted_air_gap);
		res[prefix+"utc_day_forecast"]=int2string(utc_day_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"type"]=int2string(type);
		res[prefix+"spare2"]=int2string(spare2);

		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33_Wind_V2 : public Ais8_367_33_SensorReport {
public:
	int wind_speed = 0;  // Knots
	int wind_gust = 0;  // Knots
	int wind_dir = 0;  // Degrees
	int averaging_time = 0;  // Minutes
	int sensor_type = 0;
	int wind_speed_forecast = 0;  // Knots
	int wind_gust_forecast = 0;  // Knots
	int wind_dir_forecast = 0;  // Degrees

	int utc_hour_forecast = 0;
	int utc_min_forecast = 0;

	int duration = 0;  // Minutes

	int spare2 = 0;

	Ais8_367_33_Wind_V2(const AisBitset &bs, const size_t offset);
	~Ais8_367_33_Wind_V2() {}
	Ais8_367_33_SensorEnum getType() const override {return AIS8_367_33_SENSOR_WIND_REPORT_2;}
	std::map<std::string,std::string> values(std::string prefix,std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res[prefix+"type"]="Wind_V2";

		res[prefix+"wind_speed"]=int2string(wind_speed);
		res[prefix+"wind_gust"]=int2string(wind_gust);
		res[prefix+"wind_dir"]=int2string(wind_dir);
		res[prefix+"sensor_type"]=int2string(sensor_type);
		res[prefix+"wind_gust_forecast"]=int2string(wind_gust_forecast);
		res[prefix+"wind_dir_forecast"]=int2string(wind_dir_forecast);
		res[prefix+"utc_hour_forecast"]=int2string(utc_hour_forecast);
		res[prefix+"utc_min_forecast"]=int2string(utc_min_forecast);
		res[prefix+"duration"]=int2string(duration);
		res[prefix+"spare2"]=int2string(spare2);


		return Ais8_367_33_SensorReport::values(prefix,res);
	}
};

class Ais8_367_33 : public Ais8 {
public:
	// 1 to 8 sensor reports
	vector<unique_ptr<Ais8_367_33_SensorReport>> reports;

	Ais8_367_33(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));

		int sz = reports.size();
		for (int c=0;c<sz;++c)
		{
			char bufkey[1024];
			snprintf(bufkey,1024,"reports%d_",c);
			std::string key = bufkey;
			res = reports[c]->values(key,res);
		}
		return Ais8::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais8_367_33 &msg);

class Ais9 : public AisMsg {
public:
	int alt;  // m above sea level
	int sog;
	int position_accuracy;
	AisPoint position;
	float cog;
	int timestamp;
	int alt_sensor;
	int spare;
	int dte;
	int spare2;
	int assigned_mode;
	bool raim;
	int commstate_flag;

	int sync_state;  // In both SOTDMA and ITDMA

	// SOTDMA
	bool slot_timeout_valid;
	int slot_timeout;

	// Based on slot_timeout which ones are valid
	bool received_stations_valid;
	int received_stations;

	bool slot_number_valid;
	int slot_number;

	bool utc_valid;
	int utc_hour;
	int utc_min;
	int utc_spare;

	bool slot_offset_valid;
	int slot_offset;

	// ITDMA
	bool slot_increment_valid;
	int slot_increment;

	bool slots_to_allocate_valid;
	int slots_to_allocate;

	bool keep_flag_valid;
	bool keep_flag;

	Ais9(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["alt"]=int2string(alt);
		res["sog"]=int2string(sog);
		res["position_accuracy"]=int2string(position_accuracy);
		AisPoint2string(position,"position",res);
		res["cog"]=float2string(cog);
		res["timestamp"]=int2string(timestamp);
		res["alt_sensor"]=int2string(alt_sensor);
		res["spare"]=int2string(spare);
		res["dte"]=int2string(dte);
		res["spare2"]=int2string(spare2);
		res["assigned_mode"]=int2string(assigned_mode);
		res["raim"]=bool2string(raim);
		res["commstate_flag"]=int2string(commstate_flag);
		res["sync_state"]=int2string(sync_state);
		res["slot_timeout_valid"]=bool2string(slot_timeout_valid);
		res["slot_timeout"]=int2string(slot_timeout);
		res["received_stations_valid"]=bool2string(received_stations_valid);
		res["received_stations"]=int2string(received_stations);
		res["slot_number_valid"]=bool2string(slot_number_valid);
		res["slot_number"]=int2string(slot_number);
		res["utc_valid"]=bool2string(utc_valid);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["utc_spare"]=int2string(utc_spare);
		res["slot_offset_valid"]=bool2string(slot_offset_valid);
		res["slot_offset"]=int2string(slot_offset);
		res["slot_increment_valid"]=bool2string(slot_increment_valid);
		res["slot_increment"]=int2string(slot_increment);
		res["slots_to_allocate_valid"]=bool2string(slots_to_allocate_valid);
		res["slots_to_allocate"]=int2string(slots_to_allocate);
		res["keep_flag_valid"]=bool2string(keep_flag_valid);
		res["keep_flag"]=bool2string(keep_flag);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais9 &msg);

// 10 ":" - UTC and date inquiry
class Ais10 : public AisMsg {
public:
	int spare;
	int dest_mmsi;
	int spare2;

	Ais10(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["dest_mmsi"]=int2string(dest_mmsi);
		res["spare2"]=int2string(spare2);
		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais10 &msg);

// 11 ';' - See 4_11

// 12 - '<' - Addressed safety related text.
class Ais12 : public AisMsg {
public:
	int seq_num;
	int dest_mmsi;
	bool retransmitted;
	int spare;
	string text;
	int spare2;

	Ais12(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["seq_num"]=int2string(seq_num);
		res["dest_mmsi"]=int2string(dest_mmsi);
		res["retransmitted"]=bool2string(retransmitted);
		res["spare"]=int2string(spare);
		res["text"]=string2string(text);
		res["spare2"]=int2string(spare2);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais12 &msg);

// 13 '=' - See 7

// 14 - '>' - Safety broadcast text.
class Ais14 : public AisMsg {
public:
	int spare;
	string text;
	int spare2;

	Ais14(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["text"]=string2string(text);
		res["spare2"]=int2string(spare2);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais14 &msg);

// 15 - '?' - Interrogation
class Ais15 : public AisMsg {
public:
	int spare;
	int mmsi_1;
	int msg_1_1;
	int slot_offset_1_1;

	int spare2;
	int dest_msg_1_2;
	int slot_offset_1_2;

	int spare3;
	int mmsi_2;
	int msg_2;
	int slot_offset_2;
	int spare4;

	Ais15(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["mmsi_1"]=int2string(mmsi_1);
		res["msg_1_1"]=int2string(msg_1_1);
		res["slot_offset_1_1"]=int2string(slot_offset_1_1);
		res["spare2"]=int2string(spare2);
		res["dest_msg_1_2"]=int2string(dest_msg_1_2);
		res["slot_offset_1_2"]=int2string(slot_offset_1_2);
		res["spare3"]=int2string(spare3);
		res["mmsi_2"]=int2string(mmsi_2);
		res["msg_2"]=int2string(msg_2);
		res["slot_offset_2"]=int2string(slot_offset_2);
		res["spare4"]=int2string(spare4);


		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais15 &msg);

// 16 - '@' - Assigned mode command
class Ais16 : public AisMsg {
public:
	int spare;
	int dest_mmsi_a;
	int offset_a;
	int inc_a;
	int dest_mmsi_b;
	int offset_b;
	int inc_b;
	int spare2;

	Ais16(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["dest_mmsi_a"]=int2string(dest_mmsi_a);
		res["offset_a"]=int2string(offset_a);
		res["inc_a"]=int2string(inc_a);
		res["dest_mmsi_b"]=int2string(dest_mmsi_b);
		res["offset_b"]=int2string(offset_b);
		res["inc_b"]=int2string(inc_b);
		res["spare2"]=int2string(spare2);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais16 &msg);

// ITU-R M.823  http://www.itu.int/rec/R-REC-M.823/en
// 17 - 'A' - GNSS broacast - TODO(schwehr): only partially coded
class Ais17 : public AisMsg {
public:
	int spare;
	AisPoint position;
	int spare2;
	int gnss_type;
	int z_cnt;
	int station;
	int seq;
	// N - do not need to store this
	int health;
	// TODO(schwehr): Handle payload

	Ais17(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		AisPoint2string(position,"position",res);
		res["spare2"]=int2string(spare2);
		res["gnss_type"]=int2string(gnss_type);
		res["z_cnt"]=int2string(z_cnt);
		res["station"]=int2string(station);
		res["seq"]=int2string(seq);
		res["health"]=int2string(health);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais17 &msg);


// 18 - 'B' - Class B position report
class Ais18 : public AisMsg {
public:
	int spare;
	float sog;
	int position_accuracy;
	AisPoint position;  // Long and lat
	float cog;
	int true_heading;
	int timestamp;
	int spare2;
	int unit_flag;  // 0 is SOTDMA, 1 is Carrier Sense (CS).
	int display_flag;
	int dsc_flag;
	int band_flag;
	int m22_flag;
	int mode_flag;
	bool raim;
	int commstate_flag;

	// SOTDMA
	int sync_state;
	bool slot_timeout_valid;
	int slot_timeout;

	// Based on slot_timeout which ones are valid
	bool received_stations_valid;
	int received_stations;

	bool slot_number_valid;
	int slot_number;

	bool utc_valid;
	int utc_hour;
	int utc_min;
	int utc_spare;

	bool slot_offset_valid;
	int slot_offset;

	// ITDMA
	bool slot_increment_valid;
	int slot_increment;

	bool slots_to_allocate_valid;
	int slots_to_allocate;

	bool keep_flag_valid;
	bool keep_flag;

	// If commstate set to 1 for Carrier Sense (CS) devices, there is no
	// state and the commstate region is supposed to be filled with this value:
	//   1100000000000000110
	bool commstate_cs_fill_valid;
	int commstate_cs_fill;

	Ais18(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["sog"]=float2string(sog);
		res["position_accuracy"]=int2string(position_accuracy);
		AisPoint2string(position,"position",res);
		res["cog"]=float2string(cog);
		res["true_heading"]=int2string(true_heading);
		res["timestamp"]=int2string(timestamp);
		res["spare2"]=int2string(spare2);
		res["unit_flag"]=int2string(unit_flag);
		res["display_flag"]=int2string(display_flag);
		res["dsc_flag"]=int2string(dsc_flag);
		res["band_flag"]=int2string(band_flag);
		res["m22_flag"]=int2string(m22_flag);
		res["mode_flag"]=int2string(mode_flag);
		res["raim"]=bool2string(raim);
		res["commstate_flag"]=int2string(commstate_flag);
		res["sync_state"]=int2string(sync_state);
		res["slot_timeout_valid"]=bool2string(slot_timeout_valid);
		res["slot_timeout"]=int2string(slot_timeout);
		res["received_stations_valid"]=bool2string(received_stations_valid);
		res["received_stations"]=int2string(received_stations);
		res["slot_number_valid"]=bool2string(slot_number_valid);
		res["slot_number"]=int2string(slot_number);
		res["utc_valid"]=bool2string(utc_valid);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["utc_spare"]=int2string(utc_spare);
		res["slot_offset_valid"]=bool2string(slot_offset_valid);
		res["slot_offset"]=int2string(slot_offset);
		res["slot_increment_valid"]=bool2string(slot_increment_valid);
		res["slot_increment"]=int2string(slot_increment);
		res["slots_to_allocate_valid"]=bool2string(slots_to_allocate_valid);
		res["slots_to_allocate"]=int2string(slots_to_allocate);
		res["keep_flag_valid"]=bool2string(keep_flag_valid);
		res["keep_flag"]=bool2string(keep_flag);
		res["commstate_cs_fill_valid"]=bool2string(commstate_cs_fill_valid);
		res["commstate_cs_fill"]=int2string(commstate_cs_fill);


		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais18 &msg);

// 19 - 'C' - Class B extended ship and position
class Ais19 : public AisMsg {
public:
	int spare;
	float sog;
	int position_accuracy;
	AisPoint position;  // Long and lat
	float cog;
	int true_heading;
	int timestamp;
	int spare2;
	string name;
	int type_and_cargo;
	int dim_a;
	int dim_b;
	int dim_c;
	int dim_d;
	int fix_type;
	bool raim;
	int dte;
	int assigned_mode;
	int spare3;

	Ais19(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["sog"]=float2string(sog);
		res["position_accuracy"]=int2string(position_accuracy);
		AisPoint2string(position,"position",res);
		res["cog"]=float2string(cog);
		res["true_heading"]=int2string(true_heading);
		res["timestamp"]=int2string(timestamp);
		res["spare2"]=int2string(spare2);
		res["name"]=string2string(name);
		res["type_and_cargo"]=int2string(type_and_cargo);
		res["dim_a"]=int2string(dim_a);
		res["dim_b"]=int2string(dim_b);
		res["dim_c"]=int2string(dim_c);
		res["dim_d"]=int2string(dim_d);
		res["fix_type"]=int2string(fix_type);
		res["raim"]=bool2string(raim);
		res["dte"]=int2string(dte);
		res["assigned_mode"]=int2string(assigned_mode);
		res["spare3"]=int2string(spare3);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais19 &msg);

// 20 - 'D' - Data link management
// TODO(schwehr): consider a vector
class Ais20 : public AisMsg {
public:
	int spare;
	int offset_1;
	int num_slots_1;
	int timeout_1;
	int incr_1;

	bool group_valid_2;
	int offset_2;
	int num_slots_2;
	int timeout_2;
	int incr_2;

	bool group_valid_3;
	int offset_3;
	int num_slots_3;
	int timeout_3;
	int incr_3;

	bool group_valid_4;
	int offset_4;
	int num_slots_4;
	int timeout_4;
	int incr_4;

	int spare2;

	Ais20(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["offset_1"]=int2string(offset_1);
		res["num_slots_1"]=int2string(num_slots_1);
		res["timeout_1"]=int2string(timeout_1);
		res["incr_1"]=int2string(incr_1);
		res["group_valid_2"]=bool2string(group_valid_2);
		res["offset_2"]=int2string(offset_2);
		res["num_slots_2"]=int2string(num_slots_2);
		res["timeout_2"]=int2string(timeout_2);
		res["incr_2"]=int2string(incr_2);
		res["group_valid_3"]=bool2string(group_valid_3);
		res["offset_3"]=int2string(offset_3);
		res["num_slots_3"]=int2string(num_slots_3);
		res["timeout_3"]=int2string(timeout_3);
		res["incr_3"]=int2string(incr_3);
		res["group_valid_4"]=bool2string(group_valid_4);
		res["offset_4"]=int2string(offset_4);
		res["num_slots_4"]=int2string(num_slots_4);
		res["timeout_4"]=int2string(timeout_4);
		res["incr_4"]=int2string(incr_4);
		res["spare2"]=int2string(spare2);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais20 &msg);

// 21 - 'E' - Aids to navigation report
class Ais21 : public AisMsg {
public:
	int aton_type;
	string name;
	int position_accuracy;
	AisPoint position;
	int dim_a;
	int dim_b;
	int dim_c;
	int dim_d;
	int fix_type;
	int timestamp;
	bool off_pos;
	int aton_status;
	bool raim;
	bool virtual_aton;
	bool assigned_mode;
	int spare;
	// Extended name goes on the end of name
	int spare2;

	Ais21(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["aton_type"]=int2string(aton_type);
		res["name"]=string2string(name);
		res["position_accuracy"]=int2string(position_accuracy);
		AisPoint2string(position,"position",res);
		res["dim_a"]=int2string(dim_a);
		res["dim_b"]=int2string(dim_b);
		res["dim_c"]=int2string(dim_c);
		res["dim_d"]=int2string(dim_d);
		res["fix_type"]=int2string(fix_type);
		res["timestamp"]=int2string(timestamp);
		res["off_pos"]=bool2string(off_pos);
		res["aton_status"]=int2string(aton_status);
		res["raim"]=bool2string(raim);
		res["virtual_aton"]=bool2string(virtual_aton);
		res["assigned_mode"]=bool2string(assigned_mode);
		res["spare"]=int2string(spare);
		res["spare2"]=int2string(spare2);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais21 &msg);

// 22 - 'F' - Channel Management
class Ais22 : public AisMsg {
public:
	int spare;
	int chan_a;
	int chan_b;
	int txrx_mode;
	bool power_low;

	// if addressed false, then geographic position
	bool pos_valid;
	AisPoint position1;
	AisPoint position2;

	// if addressed is true
	bool dest_valid;
	int dest_mmsi_1;
	int dest_mmsi_2;

	int chan_a_bandwidth;
	int chan_b_bandwidth;
	int zone_size;

	int spare2;  // Lame that they make a huge spare here.  Bad bad bad

	Ais22(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		res["chan_a"]=int2string(chan_a);
		res["chan_b"]=int2string(chan_b);
		res["txrx_mode"]=int2string(txrx_mode);
		res["power_low"]=bool2string(power_low);
		res["pos_valid"]=bool2string(pos_valid);
		AisPoint2string(position1,"position1",res);
		AisPoint2string(position2,"position2",res);
		res["dest_valid"]=bool2string(dest_valid);
		res["dest_mmsi_1"]=int2string(dest_mmsi_1);
		res["dest_mmsi_2"]=int2string(dest_mmsi_2);
		res["chan_a_bandwidth"]=int2string(chan_a_bandwidth);
		res["chan_b_bandwidth"]=int2string(chan_b_bandwidth);
		res["zone_size"]=int2string(zone_size);
		res["spare2"]=int2string(spare2);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais22 &msg);

// 23 - 'G' - Group Assignment Command
class Ais23 : public AisMsg {
public:
	int spare;
	AisPoint position1;
	AisPoint position2;
	int station_type;
	int type_and_cargo;

	int spare2;  // 22 bits of spare here?  what were people thinking?

	int txrx_mode;
	int interval_raw;  // raw value, not sec
	int quiet;
	int spare3;

	Ais23(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["spare"]=int2string(spare);
		AisPoint2string(position1,"position1",res);
		AisPoint2string(position2,"position2",res);
		res["station_type"]=int2string(station_type);
		res["type_and_cargo"]=int2string(type_and_cargo);
		res["spare2"]=int2string(spare2);
		res["txrx_mode"]=int2string(txrx_mode);
		res["interval_raw"]=int2string(interval_raw);
		res["quiet"]=int2string(quiet);
		res["spare3"]=int2string(spare3);


		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais23 &msg);

// 24 - 'H' - Class B Static Data report
class Ais24 : public AisMsg {
public:
	int part_num;

	// Part A
	string name;

	// Part B
	int type_and_cargo;
	string vendor_id;
	string callsign;
	int dim_a;
	int dim_b;
	int dim_c;
	int dim_d;
	int spare;

	// Part C - Not defined by ITU 1371-5
	// Part D - Not defined by ITU 1371-5

	Ais24(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["part_num"]=int2string(part_num);
		res["name"]=string2string(name);
		res["type_and_cargo"]=int2string(type_and_cargo);
		res["vendor_id"]=string2string(vendor_id);
		res["callsign"]=string2string(callsign);
		res["dim_a"]=int2string(dim_a);
		res["dim_b"]=int2string(dim_b);
		res["dim_c"]=int2string(dim_c);
		res["dim_d"]=int2string(dim_d);
		res["spare"]=int2string(spare);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais24 &msg);

// 25 - 'I' - Single slot binary message - addressed or broadcast
// TODO(schwehr): handle payload
class Ais25 : public AisMsg {
public:
	bool use_app_id;  // if false, payload is unstructured binary.

	bool dest_mmsi_valid;
	int dest_mmsi;  // only valid if addressed
	// If unstructured:
	// TODO(schwehr): vector<unsigned char> payload;

	int dac;  // valid if use_app_id is true
	int fi;

	Ais25(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["use_app_id"]=bool2string(use_app_id);
		res["dest_mmsi_valid"]=bool2string(dest_mmsi_valid);
		res["dest_mmsi"]=int2string(dest_mmsi);
		res["dac"]=int2string(dac);
		res["fi"]=int2string(fi);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais25 &msg);

// 26 - 'J' - Multi slot binary message with comm state
// TODO(schwehr): handle payload
class Ais26 : public AisMsg {
public:
	bool use_app_id;  // if false, payload is unstructured binary

	bool dest_mmsi_valid;
	int dest_mmsi;  // only valid if addressed

	int dac;  // valid it use_app_id
	int fi;

	// TODO(schwehr): vector<unsigned char> payload;  // If unstructured.  Yuck.

	int commstate_flag;  // 0 - SOTDMA, 1 - TDMA

	// SOTDMA
	int sync_state;
	bool slot_timeout_valid;
	int slot_timeout;

	// Based on slot_timeout which ones are valid
	bool received_stations_valid;
	int received_stations;

	bool slot_number_valid;
	int slot_number;

	bool utc_valid;
	int utc_hour;
	int utc_min;
	int utc_spare;

	bool slot_offset_valid;
	int slot_offset;

	// ITDMA
	bool slot_increment_valid;
	int slot_increment;

	bool slots_to_allocate_valid;
	int slots_to_allocate;

	bool keep_flag_valid;
	bool keep_flag;

	Ais26(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["use_app_id"]=bool2string(use_app_id);
		res["dest_mmsi_valid"]=bool2string(dest_mmsi_valid);
		res["dest_mmsi"]=int2string(dest_mmsi);
		res["dac"]=int2string(dac);
		res["fi"]=int2string(fi);
		res["commstate_flag"]=int2string(commstate_flag);
		res["sync_state"]=int2string(sync_state);
		res["slot_timeout_valid"]=bool2string(slot_timeout_valid);
		res["slot_timeout"]=int2string(slot_timeout);
		res["received_stations_valid"]=bool2string(received_stations_valid);
		res["received_stations"]=int2string(received_stations);
		res["slot_number_valid"]=bool2string(slot_number_valid);
		res["slot_number"]=int2string(slot_number);
		res["utc_valid"]=bool2string(utc_valid);
		res["utc_hour"]=int2string(utc_hour);
		res["utc_min"]=int2string(utc_min);
		res["utc_spare"]=int2string(utc_spare);
		res["slot_offset_valid"]=bool2string(slot_offset_valid);
		res["slot_offset"]=int2string(slot_offset);
		res["slot_increment_valid"]=bool2string(slot_increment_valid);
		res["slot_increment"]=int2string(slot_increment);
		res["slots_to_allocate_valid"]=bool2string(slots_to_allocate_valid);
		res["slots_to_allocate"]=int2string(slots_to_allocate);
		res["keep_flag_valid"]=bool2string(keep_flag_valid);
		res["keep_flag"]=bool2string(keep_flag);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais26 &msg);

// 27 - 'K' - Long-range position report - e.g. for satellite receivers
class Ais27 : public AisMsg {
public:
	int position_accuracy;
	bool raim;
	int nav_status;
	AisPoint position;
	int sog;  // Knots.
	int cog;  // Degrees.
	bool gnss;  // warning: bits in AIS are flipped sense
	int spare;

	Ais27(const char *nmea_payload, const size_t pad);
	std::map<std::string,std::string> values(std::map<std::string,std::string> super = std::map<std::string,std::string>()) const override
	{
		std::map<std::string,std::string> res(std::move(super));
		res["position_accuracy"]=int2string(position_accuracy);
		res["raim"]=bool2string(raim);
		res["nav_status"]=int2string(nav_status);
		AisPoint2string(position,"position",res);
		res["sog"]=int2string(sog);
		res["cog"]=int2string(cog);
		res["gnss"]=bool2string(gnss);
		res["spare"]=int2string(spare);

		return AisMsg::values(res);
	}
};
ostream& operator<< (ostream &o, const Ais27 &msg);

}  // namespace libais

#endif  // LIBAIS_AIS_H_
